contacts_model.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. package main
  2. import (
  3. "cmp"
  4. "encoding/json"
  5. "errors"
  6. "log"
  7. "math/rand/v2"
  8. "os"
  9. "slices"
  10. "strings"
  11. "sync"
  12. "time"
  13. )
  14. // Contact model
  15. const (
  16. PageSize = 100
  17. )
  18. type anyMap map[string]any
  19. /*
  20. # mock contacts database
  21. */
  22. var db = make(map[uint64]Contact, 0)
  23. /*
  24. class Contact:
  25. */
  26. type Contact struct {
  27. ID uint64
  28. First string
  29. Last string
  30. Phone string
  31. Email string
  32. Errors []error `json:"-"`
  33. }
  34. /*
  35. def __init__(self, id_=None, first=None, last=None, phone=None, email=None):
  36. */
  37. func NewContact(args anyMap) *Contact {
  38. c := Contact{
  39. Errors: make([]error, 0),
  40. }
  41. // Note: the "ok" are for the type assertions. The values are already set
  42. // to the 0V (or a slice for errors) at this point anyway.
  43. if id, ok := args["ID"].(uint64); ok {
  44. c.ID = id
  45. }
  46. if first, ok := args["First"].(string); ok {
  47. c.First = first
  48. }
  49. if last, ok := args["list"].(string); ok {
  50. c.Last = last
  51. }
  52. if phone, ok := args["Phone"].(string); ok {
  53. c.Phone = phone
  54. }
  55. if email, ok := args["Email"].(string); ok {
  56. c.Email = email
  57. }
  58. return &c
  59. }
  60. /*
  61. def __str__(self):
  62. */
  63. func (self *Contact) String() string {
  64. bs, err := json.Marshal(*self)
  65. if err != nil {
  66. log.Println(err)
  67. }
  68. return string(bs)
  69. }
  70. /*
  71. def update(self, first, last, phone, email):
  72. */
  73. func (self *Contact) Update(first, last, phone, email string) {
  74. self.First = first
  75. self.Last = last
  76. self.Phone = phone
  77. self.Email = email
  78. }
  79. /*
  80. def validate(self):
  81. */
  82. func (self *Contact) validate() bool {
  83. if self.Email == "" {
  84. self.Errors = append(self.Errors, errors.New("Email required"))
  85. }
  86. for id, contact := range db {
  87. if id != self.ID && contact.Email == self.Email {
  88. self.Errors = append(self.Errors, errors.New("Email Must Be Unique"))
  89. break
  90. }
  91. }
  92. return len(self.Errors) == 0
  93. }
  94. /*
  95. def save(self):
  96. */
  97. func (self *Contact) Save() bool {
  98. if !self.validate() {
  99. return false
  100. }
  101. var maxID uint64
  102. if self.ID == 0 {
  103. if len(db) == 0 {
  104. maxID = 1
  105. } else {
  106. for id := range db {
  107. if id > maxID {
  108. maxID = id
  109. }
  110. }
  111. }
  112. self.ID = maxID + 1
  113. }
  114. SaveDB()
  115. return true
  116. }
  117. /*
  118. def delete(self):
  119. */
  120. func (self *Contact) Delete() {
  121. delete(db, self.ID)
  122. }
  123. /*
  124. @classmethod
  125. def count(cls):
  126. */
  127. func (*Contact) Count() int {
  128. time.Sleep(2 * time.Second)
  129. return len(db)
  130. }
  131. /*
  132. @classmethod
  133. def all(cls, page=1):
  134. */
  135. func (*Contact) All(page int) []Contact {
  136. if page == 0 {
  137. page = 1
  138. }
  139. start := (page - 1) * PageSize
  140. end := start + PageSize
  141. ids := make([]uint64, 0, len(db))
  142. for id := range db {
  143. ids = append(ids, id)
  144. }
  145. slices.SortStableFunc(ids, cmp.Compare[uint64])
  146. pageIDs := ids[start:end]
  147. contacts := make([]Contact, 0, len(pageIDs))
  148. for _, id := range pageIDs {
  149. contacts = append(contacts, db[id])
  150. }
  151. return contacts
  152. }
  153. /*
  154. @classmethod
  155. def search(cls, text):
  156. */
  157. func (*Contact) Search(text string) []Contact {
  158. var result []Contact
  159. for _, c := range db {
  160. matchFirst := c.First != "" && strings.Contains(c.First, text)
  161. matchLast := c.Last != "" && strings.Contains(c.Last, text)
  162. matchEmail := c.Email != "" && strings.Contains(c.Email, text)
  163. matchPhone := c.Phone != "" && strings.Contains(c.Phone, text)
  164. if matchFirst || matchLast || matchEmail || matchPhone {
  165. result = append(result, c)
  166. }
  167. }
  168. return result
  169. }
  170. /*
  171. @classmethod
  172. def load_db(cls):
  173. */
  174. func (*Contact) LoadDB() {
  175. db = make(map[uint64]Contact, 0)
  176. contacts := make([]Contact, 0)
  177. bs, err := os.ReadFile((&Archiver{}).ArchiveFile())
  178. if err != nil {
  179. panic(err)
  180. }
  181. if err := json.Unmarshal(bs, &contacts); err != nil {
  182. panic(err)
  183. }
  184. for _, c := range contacts {
  185. db[c.ID] = c
  186. }
  187. }
  188. /*
  189. @staticmethod
  190. def save_db():
  191. */
  192. func SaveDB() {
  193. contacts := make([]Contact, 0, len(db))
  194. for _, contact := range db {
  195. contacts = append(contacts, contact)
  196. }
  197. slices.SortStableFunc(contacts, func(a, b Contact) int {
  198. return cmp.Compare(a.ID, b.ID)
  199. })
  200. f, err := os.Create((&Archiver{}).ArchiveFile())
  201. if err != nil {
  202. panic(err)
  203. }
  204. defer f.Close()
  205. enc := json.NewEncoder(f)
  206. if err := enc.Encode(contacts); err != nil {
  207. panic(err)
  208. }
  209. }
  210. /*
  211. @classmethod
  212. def find(cls, id_):
  213. */
  214. func (*Contact) Find(id uint64) Contact {
  215. c, ok := db[id]
  216. // TODO shouldn't it be if !ok ? Original Python logic seems wrong.
  217. if ok {
  218. c.Errors = make([]error, 0)
  219. }
  220. return c
  221. }
  222. type Archiver struct {
  223. archiveStatus string
  224. archiveProgress float64
  225. sync.RWMutex
  226. }
  227. const (
  228. statusWaiting = "Waiting"
  229. statusRunning = "Running"
  230. statusComplete = "Complete"
  231. )
  232. /*
  233. def status(self):
  234. */
  235. func (self *Archiver) Status() string {
  236. self.RLock()
  237. defer self.RUnlock()
  238. return self.archiveStatus
  239. }
  240. /*
  241. def progress(self):
  242. */
  243. func (self *Archiver) Progress() float64 {
  244. self.RLock()
  245. defer self.RUnlock()
  246. return self.archiveProgress
  247. }
  248. /*
  249. def run(self):
  250. */
  251. func (self *Archiver) Run() {
  252. self.Lock()
  253. defer self.Unlock()
  254. if self.archiveStatus == statusWaiting {
  255. self.archiveStatus = statusRunning
  256. self.archiveProgress = 0.0
  257. go self.RunImpl()
  258. }
  259. }
  260. /*
  261. def run_impl(self):
  262. */
  263. func (self *Archiver) RunImpl() {
  264. for i := range 10 {
  265. time.Sleep(time.Duration(rand.Float64()) * time.Second)
  266. self.Lock()
  267. if self.archiveStatus != statusRunning {
  268. self.Unlock()
  269. return
  270. }
  271. self.archiveProgress = float64(i+1) / 10.0
  272. log.Println("Here... ", self.archiveProgress)
  273. self.Unlock()
  274. }
  275. time.Sleep(time.Second)
  276. self.Lock()
  277. if self.archiveStatus != statusRunning {
  278. self.Unlock()
  279. return
  280. }
  281. self.archiveStatus = statusComplete
  282. self.Unlock()
  283. }
  284. /*
  285. def archive_file(self):
  286. */
  287. func (self *Archiver) ArchiveFile() string {
  288. return "contacts.json"
  289. }
  290. /*
  291. def reset(self):
  292. */
  293. func (self *Archiver) Reset() {
  294. self.Lock()
  295. defer self.Unlock()
  296. self.archiveStatus = statusWaiting
  297. self.archiveProgress = 0.0
  298. }
  299. /*
  300. @classmethod
  301. def get(cls):
  302. */
  303. func NewArchiver() *Archiver {
  304. return &Archiver{
  305. archiveStatus: statusWaiting,
  306. }
  307. }