Skip to content

Commit f13803f

Browse files
Merge pull request #85 from pitangainnovare/add/metrics-exportation
Add/metrics exportation
2 parents 4e354bb + 8f469bb commit f13803f

12 files changed

Lines changed: 577 additions & 56 deletions

File tree

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.6.0
1+
1.7.0

article/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class Article(CommonControlField):
9191
)
9292

9393
def __str__(self):
94-
return f'{self.collection.acron3} - {self.scielo_issn} - {self.pid_v2}'
94+
return f'{self.collection.acron3} - {self.scielo_issn} - {self.pid_v2 or self.pid_v3 or self.pid_generic}'
9595

9696
@classmethod
9797
def metadata(cls, collection=None):

config/settings/base.py

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@
108108
"wagtailcaptcha",
109109
"wagtailmenus",
110110
"rest_framework",
111-
"haystack",
112111
]
113112

114113
LOCAL_APPS = [
@@ -403,35 +402,11 @@
403402
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
404403
}
405404

406-
# django haystack
407-
# ------------------------------------------------------------------------------
408-
HAYSTACK_CONNECTIONS = {
409-
"default": {
410-
"ENGINE": "haystack.backends.solr_backend.SolrEngine",
411-
"URL": "%s%s" % (env("SOLR_URL", default="http://solr:8983/solr/"), "metrics"),
412-
"ADMIN_URL": "http://solr:8983/solr/admin/cores",
413-
"SILENTLY_FAIL": False,
414-
"SOLR_TIMEOUT": 10,
415-
},
416-
"metrics": {
417-
"ENGINE": "haystack.backends.solr_backend.SolrEngine",
418-
"URL": "%s%s" % (env("SOLR_URL", default="http://solr:8983/solr/"), "metrics"),
419-
"ADMIN_URL": "http://solr:8983/solr/admin/cores",
420-
"SILENTLY_FAIL": False,
421-
"SOLR_TIMEOUT": 10,
422-
},
423-
"log_manager": {
424-
"ENGINE": "haystack.backends.solr_backend.SolrEngine",
425-
"URL": "%s%s" % (env("SOLR_URL", default="http://solr:8983/solr/"), "log_manager"),
426-
"ADMIN_URL": "http://solr:8983/solr/admin/cores",
427-
"SILENTLY_FAIL": False,
428-
"SOLR_TIMEOUT": 10,
429-
},
430-
}
431-
432-
USE_SOLR = env.bool("USE_SOLR", default=False)
433-
434-
if USE_SOLR:
435-
HAYSTACK_SIGNAL_PROCESSOR = "haystack.signals.RealtimeSignalProcessor"
436-
437405
SEARCH_PAGINATION_ITEMS_PER_PAGE = 10
406+
407+
# Elasticsearch
408+
# ------------------------------------------------------------------------------
409+
ES_URL = env("ES_URL", default="http://192.168.0.33:9200/")
410+
ES_INDEX_NAME = env("ES_INDEX_NAME", default="usage-daily")
411+
ES_API_KEY = env("ES_API_KEY", default="")
412+
ES_BASIC_AUTH = env("ES_BASIC_AUTH", default=("elastic", "iHktg66E"))

config/settings/production.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,3 @@
198198
environment=env("SENTRY_ENVIRONMENT", default="production"),
199199
traces_sample_rate=env.float("SENTRY_TRACES_SAMPLE_RATE", default=0.0),
200200
)
201-
202-
# Your stuff...
203-
# ------------------------------------------------------------------------------
204-
USE_SOLR = env.bool("USE_SOLR", default=True)
205-
206-
if USE_SOLR:
207-
HAYSTACK_SIGNAL_PROCESSOR = "haystack.signals.RealtimeSignalProcessor"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Generated by Django 5.0.7 on 2025-05-18 15:48
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
("log_manager", "0002_alter_collectionconfig_unique_together_and_more"),
9+
]
10+
11+
operations = [
12+
migrations.AddField(
13+
model_name="collectionlogfiledatecount",
14+
name="is_usage_metric_computed",
15+
field=models.BooleanField(
16+
default=False, verbose_name="Is Usage Metric Computed"
17+
),
18+
),
19+
]

log_manager/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,18 @@ class CollectionLogFileDateCount(CommonControlField):
123123
blank=True,
124124
null=True,
125125
)
126+
127+
is_usage_metric_computed = models.BooleanField(
128+
verbose_name=_('Is Usage Metric Computed'),
129+
default=False,
130+
)
126131

127132
status = models.CharField(
128133
verbose_name=_('Status'),
129134
choices=choices.COLLECTION_LOG_FILE_DATE_COUNT,
130135
max_length=3,
131136
)
137+
132138
def set_status(self):
133139
if self.found_log_files < self.expected_log_files:
134140
self.status = choices.COLLECTION_LOG_FILE_DATE_COUNT_MISSING_FILES
@@ -176,6 +182,7 @@ class Meta:
176182
FieldPanel('found_log_files'),
177183
FieldPanel('expected_log_files'),
178184
FieldPanel('status'),
185+
FieldPanel('is_usage_metric_computed'),
179186
]
180187

181188
def __str__(self):

log_manager/tasks.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from core.utils.utils import _get_user
1111
from config import celery_app
1212
from collection.models import Collection
13-
from log_manager_config.models import CollectionValidationParameters
1413
from log_manager_config import exceptions as lmc_exceptions, models as lmc_models
1514

1615
from . import (

log_manager/wagtail_hooks.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ class CollectionLogFileDateCountViewSet(SnippetViewSet):
3737
list_display = (
3838
"collection",
3939
"date",
40-
"year",
41-
"month",
4240
"found_log_files",
4341
"expected_log_files",
4442
"status",
43+
"is_usage_metric_computed",
4544
)
4645
list_filter = (
4746
"collection",
4847
"status",
48+
"is_usage_metric_computed",
4949
"year",
5050
"month"
5151
)

metrics/es.py

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
from elasticsearch import Elasticsearch, helpers, NotFoundError
2+
from django.conf import settings
3+
4+
import logging
5+
6+
7+
def get_elasticsearch_client(url=None, basic_auth=None, api_key=None):
8+
"""
9+
Create an Elasticsearch client instance using Django settings.
10+
11+
:param url: Elasticsearch URL. If None, it will be taken from Django settings.
12+
:param basic_auth: Basic authentication credentials. If None, it will be taken from Django settings.
13+
:param api_key: API key. If None, it will be taken from Django settings.
14+
"""
15+
if not url:
16+
url = getattr(settings, "ES_URL", None)
17+
18+
if not basic_auth:
19+
basic_auth = getattr(settings, "ES_BASIC_AUTH", None)
20+
21+
if not api_key:
22+
api_key = getattr(settings, "ES_API_KEY", None)
23+
24+
if basic_auth:
25+
client = Elasticsearch(url, basic_auth=basic_auth)
26+
elif api_key:
27+
client = Elasticsearch(url, api_key=api_key)
28+
else:
29+
client = Elasticsearch(url)
30+
31+
return client
32+
33+
34+
def create_index(index_name, mappings=None, client=None, url=None, basic_auth=None, api_key=None):
35+
"""
36+
Create an Elasticsearch index.
37+
38+
:param index_name: Name of the index to create.
39+
:param mappings: Mappings for the index. If None, default mappings will be used.
40+
:param client: Elasticsearch client instance. If None, a new client will be created.
41+
:param url: Elasticsearch URL. If None, it will be taken from Django settings.
42+
:param basic_auth: Basic authentication credentials. If None, it will be taken from Django settings.
43+
:param api_key: API key. If None, it will be taken from Django settings.
44+
"""
45+
if not client:
46+
client = get_elasticsearch_client(url, basic_auth, api_key)
47+
48+
if not mappings:
49+
mappings = {
50+
"properties": {
51+
"collection": {
52+
"type": "keyword"
53+
},
54+
"journal": {
55+
"type": "keyword"
56+
},
57+
"pid_v2": {
58+
"type": "keyword"
59+
},
60+
"pid_v3": {
61+
"type": "keyword"
62+
},
63+
"pid_generic": {
64+
"type": "keyword"
65+
},
66+
"media_language": {
67+
"type": "keyword"
68+
},
69+
"country_code": {
70+
"type": "keyword"
71+
},
72+
"date": {
73+
"type": "date",
74+
"format": "yyyy-MM-dd"
75+
},
76+
"year": {
77+
"type": "integer"
78+
},
79+
"month": {
80+
"type": "integer"
81+
},
82+
"day": {
83+
"type": "integer"
84+
},
85+
"total_requests": {
86+
"type": "integer"
87+
},
88+
"total_investigations": {
89+
"type": "integer"
90+
},
91+
"unique_requests": {
92+
"type": "integer"
93+
},
94+
"unique_investigations": {
95+
"type": "integer"
96+
}
97+
}
98+
}
99+
100+
resp = client.indices.create(
101+
index=index_name,
102+
mappings=mappings,
103+
)
104+
logging.info(f"Index {index_name} created: {resp}")
105+
106+
107+
def delete_index(index_name, client=None, url=None, basic_auth=None, api_key=None):
108+
"""
109+
Delete an Elasticsearch index.
110+
111+
:param index_name: Name of the index to delete.
112+
:param client: Elasticsearch client instance. If None, a new client will be created.
113+
:param url: Elasticsearch URL. If None, it will be taken from Django settings.
114+
:param basic_auth: Basic authentication credentials. If None, it will be taken from Django settings.
115+
:param api_key: API key. If None, it will be taken from Django settings.
116+
"""
117+
if not client:
118+
client = get_elasticsearch_client(url, basic_auth, api_key)
119+
client.indices.delete(index=index_name)
120+
121+
122+
def index_document(index_name, doc_id, document, client=None, url=None, basic_auth=None, api_key=None):
123+
"""
124+
Index a document in Elasticsearch.
125+
126+
:param index_name: Name of the index.
127+
:param doc_id: ID of the document.
128+
:param document: Document to index.
129+
:param client: Elasticsearch client instance. If None, a new client will be created.
130+
:param url: Elasticsearch URL. If None, it will be taken from Django settings.
131+
:param basic_auth: Basic authentication credentials. If None, it will be taken from Django settings.
132+
:param api_key: API key. If None, it will be taken from Django settings.
133+
"""
134+
if not client:
135+
client = get_elasticsearch_client(url, basic_auth, api_key)
136+
client.index(index=index_name, id=doc_id, document=document)
137+
138+
139+
def index_documents(index_name, documents, client=None, url=None, basic_auth=None, api_key=None):
140+
"""
141+
Index multiple documents in Elasticsearch.
142+
143+
:param index_name: Name of the index.
144+
:param documents: Dictionary of documents to index, where keys are document IDs and values are the documents.
145+
:param client: Elasticsearch client instance. If None, a new client will be created.
146+
:param url: Elasticsearch URL. If None, it will be taken from Django settings.
147+
:param basic_auth: Basic authentication credentials. If None, it will be taken from Django settings.
148+
:param api_key: API key. If None, it will be taken from Django settings.
149+
"""
150+
if not client:
151+
client = get_elasticsearch_client(url, basic_auth, api_key)
152+
153+
helpers.bulk(
154+
client,
155+
(
156+
{
157+
"_index": index_name,
158+
"_id": doc_id,
159+
"_source": document,
160+
}
161+
for doc_id, document in documents.items()
162+
),
163+
)
164+
165+
166+
def delete_document(index_name, doc_id, client=None, url=None, basic_auth=None, api_key=None):
167+
"""
168+
Delete a document from Elasticsearch.
169+
170+
:param index_name: Name of the index.
171+
:param doc_id: ID of the document to delete.
172+
:param client: Elasticsearch client instance. If None, a new client will be created.
173+
:param url: Elasticsearch URL. If None, it will be taken from Django settings.
174+
:param basic_auth: Basic authentication credentials. If None, it will be taken from Django settings.
175+
:param api_key: API key. If None, it will be taken from Django settings.
176+
"""
177+
if not client:
178+
client = get_elasticsearch_client(url, basic_auth, api_key)
179+
180+
try:
181+
client.delete(index=index_name, id=doc_id)
182+
except NotFoundError as e:
183+
logging.error(f"Failed to delete document {doc_id} from Elasticsearch: {e}")
184+
185+
186+
def delete_documents(index_name, doc_ids, client=None, url=None, basic_auth=None, api_key=None):
187+
"""
188+
Delete multiple documents from Elasticsearch using bulk.
189+
:param index_name: Name of the index.
190+
:param doc_ids: List of document IDs to delete.
191+
:param client: Elasticsearch client instance. If None, a new client will be created.
192+
:param url: Elasticsearch URL. If None, it will be taken from Django settings.
193+
:param basic_auth: Basic authentication credentials. If None, it will be taken from Django settings.
194+
:param api_key: API key. If None, it will be taken from Django settings.
195+
"""
196+
if not client:
197+
client = get_elasticsearch_client(url, basic_auth, api_key)
198+
199+
actions = (
200+
{
201+
"_op_type": "delete",
202+
"_index": index_name,
203+
"_id": doc_id,
204+
}
205+
for doc_id in doc_ids
206+
)
207+
208+
try:
209+
helpers.bulk(client, actions)
210+
except helpers.BulkIndexError as e:
211+
logging.error(f"BulkIndexError occurred: {e.errors}")
212+
213+
214+
def delete_documents_by_key(index_name, key, values, client=None, url=None, basic_auth=None, api_key=None):
215+
"""
216+
Delete multiple documents from Elasticsearch based on a specific key and its values.
217+
218+
:param index_name: Name of the index.
219+
:param key: Key to search for in the documents.
220+
:param values: List of values to match against the key.
221+
:param client: Elasticsearch client instance. If None, a new client will be created.
222+
:param url: Elasticsearch URL. If None, it will be taken from Django settings.
223+
:param basic_auth: Basic authentication credentials. If None, it will be taken from Django settings.
224+
:param api_key: API key. If None, it will be taken from Django settings.
225+
"""
226+
if not client:
227+
client = get_elasticsearch_client(url, basic_auth, api_key)
228+
229+
query = {
230+
"query": {
231+
"terms": {
232+
key: values
233+
}
234+
}
235+
}
236+
237+
try:
238+
client.delete_by_query(index=index_name, body=query)
239+
except Exception as e:
240+
logging.error(f"Failed to delete documents by key {key} with values {values}: {e}")

0 commit comments

Comments
 (0)