inat-get 0.8.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +16 -0
  4. data/Rakefile +4 -0
  5. data/bin/inat-get +59 -0
  6. data/docs/logo.png +0 -0
  7. data/inat-get.gemspec +33 -0
  8. data/lib/extra/enum.rb +184 -0
  9. data/lib/extra/period.rb +252 -0
  10. data/lib/extra/uuid.rb +90 -0
  11. data/lib/inat/app/application.rb +50 -0
  12. data/lib/inat/app/config/messagelevel.rb +22 -0
  13. data/lib/inat/app/config/shiftage.rb +24 -0
  14. data/lib/inat/app/config/updatemode.rb +20 -0
  15. data/lib/inat/app/config.rb +296 -0
  16. data/lib/inat/app/globals.rb +80 -0
  17. data/lib/inat/app/info.rb +21 -0
  18. data/lib/inat/app/logging.rb +35 -0
  19. data/lib/inat/app/preamble.rb +27 -0
  20. data/lib/inat/app/status.rb +74 -0
  21. data/lib/inat/app/task/context.rb +47 -0
  22. data/lib/inat/app/task/dsl.rb +24 -0
  23. data/lib/inat/app/task.rb +75 -0
  24. data/lib/inat/data/api.rb +218 -0
  25. data/lib/inat/data/cache.rb +9 -0
  26. data/lib/inat/data/db.rb +87 -0
  27. data/lib/inat/data/ddl.rb +18 -0
  28. data/lib/inat/data/entity/comment.rb +29 -0
  29. data/lib/inat/data/entity/flag.rb +22 -0
  30. data/lib/inat/data/entity/identification.rb +45 -0
  31. data/lib/inat/data/entity/observation.rb +172 -0
  32. data/lib/inat/data/entity/observationphoto.rb +25 -0
  33. data/lib/inat/data/entity/observationsound.rb +26 -0
  34. data/lib/inat/data/entity/photo.rb +31 -0
  35. data/lib/inat/data/entity/place.rb +57 -0
  36. data/lib/inat/data/entity/project.rb +94 -0
  37. data/lib/inat/data/entity/projectadmin.rb +21 -0
  38. data/lib/inat/data/entity/projectobservationrule.rb +50 -0
  39. data/lib/inat/data/entity/request.rb +58 -0
  40. data/lib/inat/data/entity/sound.rb +27 -0
  41. data/lib/inat/data/entity/taxon.rb +94 -0
  42. data/lib/inat/data/entity/user.rb +67 -0
  43. data/lib/inat/data/entity/vote.rb +22 -0
  44. data/lib/inat/data/entity.rb +291 -0
  45. data/lib/inat/data/enums/conservationstatus.rb +30 -0
  46. data/lib/inat/data/enums/geoprivacy.rb +14 -0
  47. data/lib/inat/data/enums/iconictaxa.rb +23 -0
  48. data/lib/inat/data/enums/identificationcategory.rb +13 -0
  49. data/lib/inat/data/enums/licensecode.rb +16 -0
  50. data/lib/inat/data/enums/projectadminrole.rb +11 -0
  51. data/lib/inat/data/enums/projecttype.rb +37 -0
  52. data/lib/inat/data/enums/qualitygrade.rb +12 -0
  53. data/lib/inat/data/enums/rank.rb +60 -0
  54. data/lib/inat/data/model.rb +551 -0
  55. data/lib/inat/data/query.rb +1145 -0
  56. data/lib/inat/data/sets/dataset.rb +104 -0
  57. data/lib/inat/data/sets/list.rb +190 -0
  58. data/lib/inat/data/sets/listers.rb +15 -0
  59. data/lib/inat/data/sets/wrappers.rb +137 -0
  60. data/lib/inat/data/types/extras.rb +88 -0
  61. data/lib/inat/data/types/location.rb +89 -0
  62. data/lib/inat/data/types/std.rb +293 -0
  63. data/lib/inat/report/table.rb +135 -0
  64. data/lib/inat/utils/deep.rb +30 -0
  65. metadata +137 -0
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'extra/uuid'
4
+
5
+ require_relative '../types/std'
6
+ require_relative '../types/extras'
7
+ require_relative '../entity'
8
+
9
+ # require_relative 'observation'
10
+ # require_relative 'photo'
11
+
12
+ autoload :Observation, 'inat/data/entity/observation'
13
+ autoload :Photo, 'inat/data/entity/photo'
14
+
15
+ class ObservationPhoto < Entity
16
+
17
+ table :observation_photos
18
+
19
+ field :observation, type: Observation, index: true
20
+
21
+ field :uuid, type: UUID, unique: true
22
+ field :position, type: Integer, index: true
23
+ field :photo, type: Photo, required: true
24
+
25
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'extra/uuid'
4
+
5
+ require_relative '../types/std'
6
+ require_relative '../types/extras'
7
+ require_relative '../entity'
8
+
9
+ # require_relative 'observation'
10
+ # require_relative 'sound'
11
+
12
+ # class Observation < Entity; end
13
+
14
+ autoload :Observation, 'inat/data/entity/observation'
15
+ autoload :Sound, 'inat/data/entity/sound'
16
+
17
+ class ObservationSound < Entity
18
+
19
+ table :observation_sounds
20
+
21
+ field :observation, type: Observation, index: true
22
+
23
+ field :uuid, type: UUID, unique: true
24
+ field :sound, type: Sound, required: true
25
+
26
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../types/std'
4
+ require_relative '../types/extras'
5
+ require_relative '../entity'
6
+
7
+ autoload :Observation, 'inat/data/entity/observation'
8
+
9
+ class Photo < Entity
10
+
11
+ table :photos
12
+
13
+ field :license_code, type: Symbol, index: true
14
+ field :url, type: URI, required: true
15
+ field :square_url, type: URI
16
+ field :medium_url, type: URI
17
+ field :small_url, type: URI
18
+ field :large_url, type: URI
19
+ field :original_url, type: URI
20
+ field :attribution, type: String
21
+ field :hidden, type: Boolean, index: true
22
+
23
+ links :flags, item_type: Flag
24
+
25
+ ignore :original_dimensions # TODO: сделать нормальный тип
26
+
27
+ ignore :moderator_actions # TODO: разобраться
28
+ field :native_page_url, type: URI
29
+ ignore :native_photo_id
30
+
31
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../types/std'
4
+ require_relative '../types/extras'
5
+ require_relative '../types/location'
6
+ require_relative '../entity'
7
+
8
+ autoload :Observation, 'inat/data/entity/observation'
9
+
10
+ class Place < Entity
11
+
12
+ extend BySLUG
13
+
14
+ api_path :places
15
+ api_part :path
16
+ api_limit 500
17
+
18
+ table :places
19
+
20
+ field :uuid, type: UUID, unique: true
21
+ field :name, type: String, index: true, required: true
22
+ field :slug, type: Symbol, index: true
23
+ field :display_name, type: String, index: true
24
+ field :bbox_area, type: Float
25
+ field :admin_level, type: Integer, index: true
26
+ field :place_type, type: Integer, index: true
27
+ field :location, type: Location
28
+
29
+ ignore :geometry_geojson # TODO: implement
30
+ ignore :bounding_box_geojson
31
+
32
+ links :ancestor_places, item_type: Place, table_name: :place_ancestors, link_field: :ancestor_id, index: true
33
+
34
+ def === other
35
+ self.id == other.id && other.ancestor_place_ids.include?(self.id)
36
+ end
37
+
38
+ def sort_key
39
+ display_name
40
+ end
41
+
42
+ class << self
43
+
44
+ def DDL
45
+ super +
46
+ "INSERT OR REPLACE INTO places (id, uuid, name, display_name) VALUES (59614, '00000000-0000-0000-0000-000000000000', 'World', 'Весь мир');\n"
47
+ # Если верить API, то идентификатор такой есть, а записи для него нет.
48
+ # Вставляем ему искусственно некоторые данные для того, чтобы не запрашивать впустую каждый раз, как он встречается.
49
+ end
50
+
51
+ end
52
+
53
+ def to_s
54
+ "<a href=\"https://www.inaturalist.org/places/#{ id }\"><i class=\"fa fa-globe\"></i>  #{ display_name || name }</a>"
55
+ end
56
+
57
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../types/std'
4
+ require_relative '../types/extras'
5
+ require_relative '../types/location'
6
+ require_relative '../entity'
7
+ require_relative '../enums/projecttype'
8
+
9
+ # require_relative 'projectadmin'
10
+ # require_relative 'projectobservationrule'
11
+
12
+ autoload :Observation, 'inat/data/entity/observation'
13
+ autoload :User, 'inat/data/entity/user'
14
+ autoload :Place, 'inat/data/entity/place'
15
+ autoload :ProjectAdmin, 'inat/data/entity/projectadmin'
16
+ autoload :ProjectObservationRule, 'inat/data/entity/projectobservationrule'
17
+ autoload :Flag, 'inat/data/entity/flag'
18
+ autoload :Taxon, 'inat/data/entity/taxon'
19
+
20
+ class Project < Entity
21
+
22
+ extend BySLUG
23
+
24
+ api_path :projects
25
+ api_part :query
26
+ api_limit 200
27
+
28
+ table :projects
29
+
30
+ field :slug, type: Symbol, index: true
31
+ field :title, type: String, index: true, required: true
32
+ field :description, type: String
33
+ field :project_type, type: ProjectType, index: true
34
+ field :is_umbrella, type: Boolean, index: true
35
+ field :created_at, type: Time
36
+ field :updated_at, type: Time
37
+ field :observation_requirements_updated_at, type: Time
38
+ field :prefers_user_trust, type: Boolean
39
+ field :icon, type: URI
40
+ field :icon_file_name, type: String
41
+ field :header_image_url, type: URI
42
+ field :header_image_file_name, type: String
43
+ field :header_image_contain, type: Boolean
44
+ field :banner_color, type: String # TODO: сделать тип
45
+ field :hide_title, type: Boolean
46
+ field :user, type: User, index: true
47
+ field :location, type: Location
48
+
49
+ field :place, type: Place, index: true
50
+
51
+ # links :users, item_type: User
52
+ # TODO: разобраться как грузить пачками
53
+ ignore :users
54
+ ignore :user_ids
55
+
56
+ backs :admins, item_type: ProjectAdmin, owned: true
57
+ backs :project_observation_rules, item_type: ProjectObservationRule, owned: true
58
+
59
+ links :flags, item_type: Flag, index: true
60
+
61
+ links :subprojects, item_type: Project, table_name: :project_children, link_field: :child_id, owned: false
62
+
63
+ links :included_taxa, item_type: Taxon, ids_field: :included_taxon_ids, table_name: :project_rule_taxa, owned: false
64
+ links :excluded_taxa, item_type: Taxon, ids_field: :excluded_taxon_ids, table_name: :project_rule_excluded_taxa, owned: false
65
+ links :included_places, item_type: Place, ids_field: :included_place_ids, table_name: :project_rule_places, owned: false
66
+ links :excluded_places, item_type: Place, ids_field: :excluded_places_ids, table_name: :project_rule_excluded_places, owned: false
67
+
68
+ links :observations, item_type: Observation, table_name: :project_observations, owned: false
69
+
70
+ ignore :latitude
71
+ ignore :longitude
72
+
73
+ # block :latitude, type: Float do |value|
74
+ # @location ||= Location::new nil, nil
75
+ # @location.latitude = value
76
+ # end
77
+
78
+ # block :longitude, type: Float do |value|
79
+ # @location ||= Location::new nil, nil
80
+ # @location.longitude = value
81
+ # end
82
+
83
+ # # TODO: разобраться и сделать связи с местами и таксонами
84
+ ignore :project_observation_fields
85
+ ignore :site_features
86
+ ignore :terms
87
+ ignore :search_parameters
88
+ ignore :rule_preferences
89
+
90
+ def to_s
91
+ "<a href=\"https://www.inaturalist.org/projects/#{ id }\"><i class=\"fa fa-briefcase\"></i>  #{ title }</a>"
92
+ end
93
+
94
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../types/std'
4
+ require_relative '../types/extras'
5
+ require_relative '../entity'
6
+ require_relative '../enums/projectadminrole'
7
+
8
+ # require_relative 'project'
9
+ # autoload :Project, 'inat/data/entity/project'
10
+
11
+ class Project < Entity; end
12
+
13
+ class ProjectAdmin < Entity
14
+
15
+ table :project_admins
16
+
17
+ field :project, type: Project, index: true
18
+ field :role, type: ProjectAdminRole, index: true, required: true
19
+ field :user, type: User, index: true, required: true
20
+
21
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../types/std'
4
+ require_relative '../types/extras'
5
+ require_relative '../entity'
6
+
7
+ # require_relative 'project'
8
+ # autoload :Project, 'inat/data/entity/project'
9
+
10
+ class Project < Entity; end
11
+
12
+ class ProjectObservationRule < Entity
13
+
14
+ table :project_observation_rules
15
+
16
+ field :project, type: Project, index: true
17
+
18
+ field :operator, type: Symbol, index: true, required: true
19
+ field :operand_type, type: Symbol, index: true
20
+ field :operand_id, type: Integer, index: true
21
+
22
+ class << self
23
+
24
+ def DDL
25
+ super +
26
+ "CREATE VIEW IF NOT EXISTS project_children AS\n" +
27
+ " SELECT project_id, operand_id as child_id\n" +
28
+ " FROM project_observation_rules\n" +
29
+ " WHERE operator = 'in_project?' AND operand_type = 'Project';\n" +
30
+ "CREATE VIEW IF NOT EXISTS project_rule_taxa AS\n" +
31
+ " SELECT project_id, operand_id as taxon_id\n" +
32
+ " FROM project_observation_rules\n" +
33
+ " WHERE operator = 'in_taxon?' AND operand_type = 'Taxon';\n" +
34
+ "CREATE VIEW IF NOT EXISTS project_rule_places AS\n" +
35
+ " SELECT project_id, operand_id as place_id\n" +
36
+ " FROM project_observation_rules\n" +
37
+ " WHERE operator = 'observed_in_place?' AND operand_type = 'Place';\n" +
38
+ "CREATE VIEW IF NOT EXISTS project_rule_excluded_taxa AS\n" +
39
+ " SELECT project_id, operand_id as taxon_id\n" +
40
+ " FROM project_observation_rules\n" +
41
+ " WHERE operator = 'not_in_taxon?' AND operand_type = 'Taxon';\n" +
42
+ "CREATE VIEW IF NOT EXISTS project_rule_excluded_places AS\n" +
43
+ " SELECT project_id, operand_id as place_id\n" +
44
+ " FROM project_observation_rules\n" +
45
+ " WHERE operator = 'not_observed_in_place?' AND operand_type = 'Place';\n"
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../types/std'
4
+ require_relative '../types/extras'
5
+ require_relative '../entity'
6
+ require_relative '../db'
7
+
8
+ autoload :Observation, 'inat/data/entity/observation'
9
+ autoload :Project, 'inat/data/entity/project'
10
+
11
+ class Request < Entity
12
+
13
+ table :requests
14
+
15
+ field :time, type: Time, index: true
16
+ field :query, type: String, unique: true
17
+
18
+ field :project, type: Project, index: true
19
+
20
+ links :observations, item_type: Observation, index: true
21
+
22
+ class << self
23
+
24
+ def DDL
25
+ super +
26
+ "CREATE VIEW IF NOT EXISTS project_observations AS\n" +
27
+ " SELECT r.project_id, ro.observation_id\n" +
28
+ " FROM requests r, request_observations ro\n" +
29
+ " WHERE r.id = ro.request_id AND r.project_id IS NOT NULL;\n"
30
+ end
31
+
32
+ def create query_string, project_id
33
+ update do
34
+ max_id = DB.execute("SELECT max(id) AS id FROM requests;").map{ |r| r['id'] }.first
35
+ new_id = if max_id == nil
36
+ 1
37
+ else
38
+ max_id + 1
39
+ end
40
+ @entities ||= {}
41
+ @entities[new_id] ||= new new_id
42
+ @entities[new_id].time = Time::at(0)
43
+ @entities[new_id].query = query_string
44
+ @entities[new_id].project_id = project_id
45
+ @entities[new_id].save
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ attr_accessor :active
52
+
53
+ def initialize id
54
+ super(id)
55
+ @active = false
56
+ end
57
+
58
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../types/std'
4
+ require_relative '../types/extras'
5
+ require_relative '../entity'
6
+
7
+ autoload :Observation, 'inat/data/entity/observation'
8
+
9
+ class Sound < Entity
10
+
11
+ table :sounds
12
+
13
+ field :license_code, type: LicenseCode, index: true
14
+ field :attribution, type: String
15
+ field :file_url, type: URI
16
+ field :file_content_type, type: Symbol, index: true
17
+ field :play_local, type: Boolean
18
+ field :subtype, type: Symbol, index: true
19
+ field :hidden, type: Boolean, index: true
20
+
21
+ links :flags, item_type: Flag
22
+
23
+ ignore :native_sound_id # TODO: разобраться
24
+ ignore :secret_token
25
+ ignore :moderator_actions
26
+
27
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../types/std'
4
+ require_relative '../types/extras'
5
+ require_relative '../entity'
6
+
7
+ require_relative '../enums/rank'
8
+ require_relative '../enums/iconictaxa'
9
+
10
+ require_relative 'photo'
11
+
12
+ autoload :Observation, 'inat/data/entity/observation'
13
+ # autoload :Photo, 'inat/data/entity/photo'
14
+
15
+ class Taxon < Entity
16
+
17
+ api_path :taxa
18
+ api_part :query
19
+ api_limit 200
20
+
21
+ table :taxa
22
+
23
+ field :is_active, type: Boolean, index: true
24
+ # field :ancestry, type: Ancestry
25
+ # field :min_species_ancestry, type: Ancestry
26
+ field :endemic, type: Boolean
27
+ field :iconic_taxon, type: Taxon
28
+ field :min_species_taxon, type: Taxon
29
+ field :threatened, type: Boolean, index: true
30
+ field :rank_level, type: Float, index: true
31
+ field :introduced, type: Boolean
32
+ field :native, type: Boolean
33
+ field :parent, type: Taxon
34
+ field :name, type: String, index: true, required: true
35
+ field :rank, type: Rank, index: true
36
+ field :extinct, type: Boolean
37
+ field :created_at, type: Time
38
+ field :default_photo, type: Photo
39
+ field :taxon_changes_count, type: Integer
40
+ field :taxon_schemes_count, type: Integer
41
+ # field :observations_count, type: Integer
42
+ field :photos_locked, type: Boolean
43
+ # field :universal_search_rank, type: Integer
44
+ field :wikipedia_url, type: URI
45
+ field :wikipedia_summary, type: String
46
+ field :iconic_taxon_name, type: IconicTaxa, index: true
47
+ # field :iconic_taxon, type: Taxon, index: true
48
+ field :preferred_common_name, type: String
49
+ field :english_common_name, type: String
50
+ field :vision, type: Boolean
51
+
52
+ links :ancestors, item_type: Taxon, link_field: :ancestor_id
53
+ links :taxon_photos, item_type: Photo, table_name: :taxon_photos
54
+
55
+ backs :children, item_type: Taxon, ids_field: :child_ids, back_field: :parent_id, owned: false
56
+
57
+ ignore :flag_counts # TODO: разобраться
58
+ ignore :current_synonymous_taxon_ids
59
+ ignore :atlas_id
60
+ ignore :complete_species_count
61
+ ignore :conservation_statuses
62
+ ignore :conservation_status
63
+ ignore :listed_taxa
64
+ ignore :complete_rank
65
+
66
+ ignore :ancestry # TODO: подумать и сделать обязательно
67
+ ignore :min_species_ancestry
68
+ ignore :establishment_means
69
+ ignore :preferred_establishment_means
70
+
71
+ ignore :observations_count
72
+ ignore :universal_search_rank
73
+
74
+ def === other
75
+ other.id == self.id || other.ancestor_ids.include?(self.id)
76
+ end
77
+
78
+ def sort_key
79
+ [ iconic_taxon_name || IconicTaxa::UNKNOWN, name ]
80
+ end
81
+
82
+ private def prepare_name name
83
+ name.downcase.gsub(' ', ' ').capitalize
84
+ end
85
+
86
+ def to_s
87
+ if preferred_common_name
88
+ "<a href=\"https://www.inaturalist.org/taxa/#{ id }\"><i class=\"icon-iconic-#{ (iconic_taxon_name || IconicTaxa::UNKNOWN).to_s.downcase }\" style=\"font-size:1.5em;height:1em;line-height:1em;\"></i> #{ prepare_name(preferred_common_name) } <i>(#{ prepare_name(name) })</i></a>"
89
+ else
90
+ "<a href=\"https://www.inaturalist.org/taxa/#{ id }\"><i class=\"icon-iconic-#{ (iconic_taxon_name || IconicTaxa::UNKNOWN).to_s.downcase }\" style=\"font-size:1.5em;height:1em;line-height:1em;\"></i> <i>#{ prepare_name(name) }</i></a>"
91
+ end
92
+ end
93
+
94
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../types/std'
4
+ require_relative '../types/extras'
5
+ require_relative '../entity'
6
+
7
+ autoload :Observation, 'inat/data/entity/observation'
8
+
9
+ class User < Entity
10
+
11
+ api_path :users
12
+ api_part :path
13
+ api_limit 1
14
+
15
+ table :users
16
+
17
+ field :login, type: String, index: true, required: true
18
+ field :spam, type: Boolean
19
+ field :suspended, type: Boolean, index: true
20
+ field :created_at, type: Time, index: true
21
+ field :site_id, type: Integer, index: true
22
+ field :login_autocomplete, type: String
23
+ field :login_exact, type: String
24
+ field :name, type: String, index: true
25
+ field :name_autocomplete, type: String
26
+ field :orcid, type: URI
27
+ field :icon, type: URI
28
+ field :icon_url, type: URI
29
+ field :observations_count, type: Integer
30
+ field :identifications_count, type: Integer
31
+ field :journal_posts_count, type: Integer
32
+ field :activity_count, type: Integer
33
+ field :species_count, type: Integer
34
+ field :universal_search_rank, type: Integer
35
+
36
+ ignore :roles # TODO: разобраться
37
+ ignore :preferences
38
+
39
+ def sort_key
40
+ login
41
+ end
42
+
43
+ def self.by_login login
44
+ @entities ||= {}
45
+ results = @entities.values.select { |e| e.login == login.to_s }
46
+ if results.empty?
47
+ data = DB.execute "SELECT * FROM users WHERE login = ?", login.to_s
48
+ results = from_db_rows data
49
+ end
50
+ if results.empty?
51
+ data = API.query 'users/autocomplete', first_only: true, q: login
52
+ results = data.select { |u| u['login'] == login.to_s }.map { |d| parse(d) }
53
+ end
54
+ if results.empty?
55
+ nil
56
+ else
57
+ results.first
58
+ end
59
+ end
60
+
61
+ def to_s
62
+ title = ''
63
+ title = " title=\"#{ name }\"" if name
64
+ "<a#{ title } href=\"https://www.inaturalist.org/people/#{ id }\"><i class=\"glyphicon glyphicon-user\"></i></a> @#{ login }"
65
+ end
66
+
67
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../types/std'
4
+ require_relative '../types/extras'
5
+ require_relative '../entity'
6
+
7
+ autoload :Observation, 'inat/data/entity/observation'
8
+ autoload :User, 'inat/data/entity/user'
9
+
10
+ class Vote < Entity
11
+
12
+ table :votes
13
+
14
+ # field :observation, type: Observation, index: true, required: true
15
+
16
+ field :created_at, type: Time, index: true, required: true
17
+ field :vote_flag, type: Boolean, index: true
18
+ field :user, type: User, index: true
19
+
20
+ ignore :vote_scope # TODO: разобраться
21
+
22
+ end