contacts_model.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. package main
  2. import (
  3. "cmp"
  4. "encoding/json"
  5. "errors"
  6. "log"
  7. "math/rand"
  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 `json:"id"`
  28. First string `json:"first"`
  29. Last string `json:"last"`
  30. Phone string `json:"phone"`
  31. Email string `json:"email"`
  32. Errors []error `json:"errors"`
  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["last"].(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.Printf("Error marshaling contact: %v", 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. db[self.ID] = *self
  115. SaveDB()
  116. return true
  117. }
  118. /*
  119. def delete(self):
  120. */
  121. func (self *Contact) Delete() {
  122. delete(db, self.ID)
  123. }
  124. /*
  125. @classmethod
  126. def count(cls):
  127. */
  128. func (*Contact) Count() int {
  129. time.Sleep(2 * time.Second)
  130. return len(db)
  131. }
  132. /*
  133. @classmethod
  134. def all(cls, page=1):
  135. */
  136. func (*Contact) All(page int) []Contact {
  137. if page == 0 {
  138. page = 1
  139. }
  140. start := (page - 1) * PageSize
  141. end := min(start+PageSize, start+len(db))
  142. ids := make([]uint64, 0, len(db))
  143. for id := range db {
  144. ids = append(ids, id)
  145. }
  146. slices.SortStableFunc(ids, cmp.Compare[uint64])
  147. pageIDs := ids[start:end]
  148. contacts := make([]Contact, 0, len(pageIDs))
  149. for _, id := range pageIDs {
  150. contacts = append(contacts, db[id])
  151. }
  152. return contacts
  153. }
  154. /*
  155. @classmethod
  156. def search(cls, text):
  157. */
  158. func (*Contact) Search(text string) []Contact {
  159. var result []Contact
  160. for _, c := range db {
  161. matchFirst := c.First != "" && strings.Contains(c.First, text)
  162. matchLast := c.Last != "" && strings.Contains(c.Last, text)
  163. matchEmail := c.Email != "" && strings.Contains(c.Email, text)
  164. matchPhone := c.Phone != "" && strings.Contains(c.Phone, text)
  165. if matchFirst || matchLast || matchEmail || matchPhone {
  166. result = append(result, c)
  167. }
  168. }
  169. return result
  170. }
  171. /*
  172. @classmethod
  173. def load_db(cls):
  174. */
  175. func (*Contact) LoadDB() {
  176. db = make(map[uint64]Contact, 0)
  177. contacts := make([]Contact, 0)
  178. bs, err := os.ReadFile((&Archiver{}).ArchiveFile())
  179. if err != nil {
  180. panic(err)
  181. }
  182. if err := json.Unmarshal(bs, &contacts); err != nil {
  183. panic(err)
  184. }
  185. for _, c := range contacts {
  186. db[c.ID] = c
  187. }
  188. }
  189. /*
  190. @staticmethod
  191. def save_db():
  192. */
  193. func SaveDB() {
  194. contacts := make([]Contact, 0, len(db))
  195. for _, contact := range db {
  196. contacts = append(contacts, contact)
  197. }
  198. slices.SortStableFunc(contacts, func(a, b Contact) int {
  199. return cmp.Compare(a.ID, b.ID)
  200. })
  201. f, err := os.Create((&Archiver{}).ArchiveFile())
  202. if err != nil {
  203. panic(err)
  204. }
  205. defer f.Close()
  206. enc := json.NewEncoder(f)
  207. if err := enc.Encode(contacts); err != nil {
  208. panic(err)
  209. }
  210. }
  211. /*
  212. @classmethod
  213. def find(cls, id_):
  214. */
  215. func (*Contact) Find(id uint64) Contact {
  216. c, ok := db[id]
  217. // TODO shouldn't it be if !ok ? Original Python logic seems wrong.
  218. if ok {
  219. c.Errors = make([]error, 0)
  220. }
  221. return c
  222. }
  223. type Archiver struct {
  224. archiveStatus string
  225. archiveProgress float64
  226. sync.RWMutex
  227. }
  228. const (
  229. statusWaiting = "Waiting"
  230. statusRunning = "Running"
  231. statusComplete = "Complete"
  232. )
  233. var (
  234. archiver *Archiver
  235. )
  236. /*
  237. def status(self):
  238. */
  239. func (self *Archiver) Status() string {
  240. self.RLock()
  241. defer self.RUnlock()
  242. return self.archiveStatus
  243. }
  244. /*
  245. def progress(self):
  246. */
  247. func (self *Archiver) Progress() float64 {
  248. self.RLock()
  249. defer self.RUnlock()
  250. return self.archiveProgress
  251. }
  252. /*
  253. def run(self):
  254. */
  255. func (self *Archiver) Run() {
  256. self.Lock()
  257. defer self.Unlock()
  258. if self.archiveStatus == statusWaiting {
  259. self.archiveStatus = statusRunning
  260. self.archiveProgress = 0.0
  261. go self.RunImpl()
  262. }
  263. }
  264. /*
  265. def run_impl(self):
  266. */
  267. func (self *Archiver) RunImpl() {
  268. t0 := time.Now()
  269. for i := range 10 {
  270. time.Sleep(time.Duration(float64(time.Second) * rand.Float64()))
  271. self.Lock()
  272. if self.archiveStatus != statusRunning {
  273. self.Unlock()
  274. return
  275. }
  276. self.archiveProgress = float64(i+1) / 10
  277. log.Printf("Here... after %v: %f", time.Since(t0), self.archiveProgress)
  278. self.Unlock()
  279. }
  280. time.Sleep(time.Second)
  281. self.Lock()
  282. if self.archiveStatus != statusRunning {
  283. self.Unlock()
  284. return
  285. }
  286. self.archiveStatus = statusComplete
  287. self.Unlock()
  288. log.Printf("RunImpl took %v", time.Since(t0))
  289. }
  290. /*
  291. def archive_file(self):
  292. */
  293. func (self *Archiver) ArchiveFile() string {
  294. return "contacts.json"
  295. }
  296. /*
  297. def reset(self):
  298. */
  299. func (self *Archiver) Reset() {
  300. self.Lock()
  301. defer self.Unlock()
  302. self.archiveStatus = statusWaiting
  303. self.archiveProgress = 0.0
  304. }
  305. /*
  306. @classmethod
  307. def get(cls):
  308. */
  309. func GetArchiver() *Archiver {
  310. if archiver == nil {
  311. archiver = &Archiver{
  312. archiveStatus: statusWaiting,
  313. }
  314. }
  315. return archiver
  316. }