44 | {% endblock %}
45 | {% block js %}{% endblock %}
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/events/search_commands/qs/delete.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2025 All rights reserved.
2 | # This file is part of the Delve project, which is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
3 | # See the LICENSE file in the root of this repository for details.
4 |
5 | import logging
6 | import argparse
7 | from typing import Any, Dict, List
8 |
9 | from django.db.models.query import QuerySet
10 | from django.http import HttpRequest
11 |
12 | from events.search_commands.decorators import search_command
13 | from events.search_commands.util import has_permission_for_model
14 |
15 | delete_parser = argparse.ArgumentParser(
16 | prog="delete",
17 | description="Delete records from the QuerySet",
18 | )
19 |
20 | @search_command(delete_parser)
21 | def delete(request: HttpRequest, events: QuerySet, argv: List[str], environment: Dict[str, Any]) -> int:
22 | """
23 | Delete records from the QuerySet.
24 |
25 | Args:
26 | request (HttpRequest): The HTTP request object.
27 | events (QuerySet): The QuerySet to operate on.
28 | argv (List[str]): List of command-line arguments.
29 | environment (Dict[str, Any]): Dictionary used as a jinja2 environment (context) for rendering the arguments of a command.
30 |
31 | Returns:
32 | int: The number of records deleted.
33 | """
34 | log = logging.getLogger(__name__)
35 | log.info("In delete")
36 | args = delete_parser.parse_args(argv[1:])
37 |
38 | if not isinstance(events, QuerySet):
39 | log.critical(f"Type QuerySet expected, received {type(events)}")
40 | raise ValueError(
41 | f"delete can only operate on QuerySets like "
42 | "the output of the search command"
43 | )
44 |
45 | model = events.query.model
46 | if not has_permission_for_model('delete', model, request):
47 | raise PermissionError("Permission denied")
48 |
49 |
50 | log.debug(f"Received {args=}")
51 | deleted_count, _ = events.delete()
52 | return deleted_count
53 |
--------------------------------------------------------------------------------
/templates/project-base.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 | {% load django_bootstrap5 %}
3 |
4 |
5 |
6 |
7 |
44 | {% endblock %}
45 | {% block js %}{% endblock %}
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/events/migrations/0002_initial.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2025 All rights reserved.
2 | # This file is part of the Delve project, which is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
3 | # See the LICENSE file in the root of this repository for details.
4 |
5 | # Generated by Django 5.1.4 on 2025-01-16 18:20
6 |
7 | import django.db.models.deletion
8 | from django.conf import settings
9 | from django.db import migrations, models
10 |
11 |
12 | class Migration(migrations.Migration):
13 |
14 | initial = True
15 |
16 | dependencies = [
17 | ('events', '0001_initial'),
18 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
19 | ]
20 |
21 | operations = [
22 | migrations.AddField(
23 | model_name='event',
24 | name='user',
25 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='events', to=settings.AUTH_USER_MODEL),
26 | ),
27 | migrations.AddField(
28 | model_name='fileupload',
29 | name='user',
30 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='file_uploads', to=settings.AUTH_USER_MODEL),
31 | ),
32 | migrations.AddField(
33 | model_name='globalcontext',
34 | name='user',
35 | field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='global_context', to=settings.AUTH_USER_MODEL),
36 | ),
37 | migrations.AddField(
38 | model_name='localcontext',
39 | name='user',
40 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='local_contexts', to=settings.AUTH_USER_MODEL),
41 | ),
42 | migrations.AddField(
43 | model_name='query',
44 | name='user',
45 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='queries', to=settings.AUTH_USER_MODEL),
46 | ),
47 | ]
48 |
--------------------------------------------------------------------------------
/events/search_commands/echo.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2025 All rights reserved.
2 | # This file is part of the Delve project, which is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
3 | # See the LICENSE file in the root of this repository for details.
4 |
5 | import argparse
6 | import logging
7 | from typing import Any, Dict, List, Union
8 |
9 | from django.db.models.query import QuerySet
10 | from django.http import HttpRequest
11 |
12 | from .decorators import search_command
13 |
14 | parser = argparse.ArgumentParser(
15 | prog="echo",
16 | description="Return the given expressions as an event appended to the result set.",
17 | )
18 | parser.add_argument(
19 | nargs="+",
20 | dest="expressions",
21 | help="The expressions to return as events",
22 | )
23 |
24 | @search_command(parser)
25 | def echo(request: HttpRequest, events: Union[QuerySet, List[Dict[str, Any]]], argv: List[str], environment: Dict[str, Any]) -> List[Dict[str, Any]]:
26 | """
27 | Return the given expressions as an event appended to the result set.
28 |
29 | Args:
30 | request (HttpRequest): The HTTP request object.
31 | events (Union[QuerySet, List[Dict[str, Any]]]): The result set to operate on.
32 | argv (List[str]): List of command-line arguments.
33 | environment (Dict[str, Any]): Dictionary used as a jinja2 environment (context) for rendering the arguments of a command.
34 |
35 | Returns:
36 | List[Dict[str, Any]]: A list of dictionaries with the given expressions as events.
37 | """
38 | log = logging.getLogger(__name__)
39 | args = echo.parser.parse_args(argv[1:])
40 | log.debug(f"Found environment: {environment}")
41 | ret = []
42 | for expression in args.expressions:
43 | log.debug(f"Found expression: {type(expression)}({expression})")
44 | ret.append(
45 | {
46 | "expression": expression,
47 | }
48 | )
49 | for event in events:
50 | yield event
51 | for item in ret:
52 | yield item
--------------------------------------------------------------------------------
/events/search_commands/qs/defer.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2025 All rights reserved.
2 | # This file is part of the Delve project, which is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
3 | # See the LICENSE file in the root of this repository for details.
4 |
5 | import logging
6 | import argparse
7 | from typing import Any, Dict, List
8 |
9 | from django.db.models.query import QuerySet
10 | from django.http import HttpRequest
11 |
12 | from events.search_commands.decorators import search_command
13 | from ._util import parse_field_expressions
14 |
15 | defer_parser = argparse.ArgumentParser(
16 | prog="defer",
17 | description="Defer the loading of the specified fields",
18 | )
19 | defer_parser.add_argument(
20 | "field_expressions",
21 | nargs="*",
22 | help="The fields to defer loading",
23 | )
24 |
25 | @search_command(defer_parser)
26 | def defer(request: HttpRequest, events: QuerySet, argv: List[str], environment: Dict[str, Any]) -> QuerySet:
27 | """
28 | Defer the loading of the specified fields.
29 |
30 | Args:
31 | request (HttpRequest): The HTTP request object.
32 | events (QuerySet): The QuerySet to operate on.
33 | argv (List[str]): List of command-line arguments.
34 | environment (Dict[str, Any]): Dictionary used as a jinja2 environment (context) for rendering the arguments of a command.
35 |
36 | Returns:
37 | QuerySet: A QuerySet with deferred fields.
38 | """
39 | log = logging.getLogger(__name__)
40 | log.info("In defer")
41 | args = defer_parser.parse_args(argv[1:])
42 |
43 | if not isinstance(events, QuerySet):
44 | log.critical(f"Type QuerySet expected, received {type(events)}")
45 | raise ValueError(
46 | f"defer can only operate on QuerySets like "
47 | "the output of the search command"
48 | )
49 |
50 | log.debug(f"Received {args=}")
51 | parsed_expressions = parse_field_expressions(args.field_expressions)
52 | log.debug(f"Parsed expressions: {parsed_expressions}")
53 | return events.defer(*parsed_expressions)
54 |
--------------------------------------------------------------------------------
/events/search_commands/send_email.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2025 All rights reserved.
2 | # This file is part of the Delve project, which is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
3 | # See the LICENSE file in the root of this repository for details.
4 |
5 | from django.core.mail import send_mail
6 | from typing import Any, Dict, List, Union
7 | from django.http import HttpRequest
8 | import argparse
9 | import logging
10 |
11 | from .decorators import search_command
12 |
13 | parser = argparse.ArgumentParser(
14 | prog="send_email",
15 | description="Send an email notification based on the result set",
16 | )
17 | parser.add_argument(
18 | "recipient",
19 | type=str,
20 | help="The recipient email address",
21 | )
22 | parser.add_argument(
23 | "subject",
24 | type=str,
25 | help="The email subject",
26 | )
27 | parser.add_argument(
28 | "message",
29 | type=str,
30 | nargs="+",
31 | help="The email message",
32 | )
33 |
34 | @search_command(parser)
35 | def send_email(request: HttpRequest, events: Union[List[Dict[str, Any]], Any], argv: List[str], context: Dict[str, Any]) -> Union[List[Dict[str, Any]], Any]:
36 | """
37 | Send an email notification based on the result set.
38 |
39 | Args:
40 | request (HttpRequest): The HTTP request object.
41 | events (Union[List[Dict[str, Any]], Any]): The list of events.
42 | argv (List[str]): The list of command arguments.
43 | context (Dict[str, Any]): The context dictionary.
44 |
45 | Returns:
46 | Union[List[Dict[str, Any]], Any]: The list of events.
47 | """
48 | log = logging.getLogger(__name__)
49 | log.debug(f"Found events: {events}")
50 | args = send_email.parser.parse_args(argv[1:])
51 | log.debug(f"Found args: {args}")
52 |
53 | if not events:
54 | return events
55 |
56 |
57 | send_mail(
58 | subject=args.subject,
59 | message=' '.join(args.message),
60 | from_email='no-reply@yourdomain.com',
61 | recipient_list=[args.recipient],
62 | )
63 |
64 | return events
65 |
--------------------------------------------------------------------------------
/events/search_commands/qs/only.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2025 All rights reserved.
2 | # This file is part of the Delve project, which is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
3 | # See the LICENSE file in the root of this repository for details.
4 |
5 | import logging
6 | import argparse
7 | from typing import Any, Dict, List
8 |
9 | from django.db.models.query import QuerySet
10 | from django.http import HttpRequest
11 |
12 | from events.search_commands.decorators import search_command
13 | from ._util import parse_field_expressions
14 |
15 | only_parser = argparse.ArgumentParser(
16 | prog="only",
17 | description="Load only the specified fields on the QuerySet",
18 | )
19 |
20 | only_parser.add_argument(
21 | "fields",
22 | nargs="*",
23 | help="The fields to load on the QuerySet",
24 | )
25 |
26 | @search_command(only_parser)
27 | def only(request: HttpRequest, events: QuerySet, argv: List[str], environment: Dict[str, Any]) -> QuerySet:
28 | """
29 | Load only the specified fields on the QuerySet.
30 |
31 | Args:
32 | request (HttpRequest): The HTTP request object.
33 | events (QuerySet): The QuerySet to operate on.
34 | argv (List[str]): List of command-line arguments.
35 | environment (Dict[str, Any]): Dictionary used as a jinja2 environment (context) for rendering the arguments of a command.
36 |
37 | Returns:
38 | QuerySet: A QuerySet with only the specified fields loaded.
39 | """
40 | log = logging.getLogger(__name__)
41 | log.info("In only")
42 | args = only_parser.parse_args(argv[1:])
43 |
44 | if not isinstance(events, QuerySet):
45 | log.critical(f"Type QuerySet expected, received {type(events)}")
46 | raise ValueError(
47 | f"only can only operate on QuerySets like "
48 | "the output of the search command"
49 | )
50 |
51 | log.debug(f"Received {args=}")
52 | parsed_expressions = parse_field_expressions(args.fields)
53 | log.debug(f"Parsed expressions: {parsed_expressions}")
54 | return events.only(*parsed_expressions)
55 |
--------------------------------------------------------------------------------
/events/search_commands/replace.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2025 All rights reserved.
2 | # This file is part of the Delve project, which is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
3 | # See the LICENSE file in the root of this repository for details.
4 |
5 | import re
6 | import argparse
7 | from typing import Any, Dict, List, Union
8 |
9 | from django.db.models.query import QuerySet
10 | from django.http import HttpRequest
11 |
12 | from events.util import resolve
13 |
14 | from .decorators import search_command
15 |
16 | parser = argparse.ArgumentParser(
17 | prog="replace",
18 | description="Replace text matching a regular expression with a provided string.",
19 | )
20 | parser.add_argument(
21 | "-f",
22 | "--field",
23 | help="The field to perform the replacement on",
24 | )
25 | parser.add_argument(
26 | "expression",
27 | help="The regular expression to match",
28 | )
29 | parser.add_argument(
30 | "replacement",
31 | help="The string to replace the matched text with",
32 | )
33 |
34 | @search_command(parser)
35 | def replace(request: HttpRequest, events: Union[QuerySet, List[Dict[str, Any]]], argv: List[str], environment: Dict[str, Any]) -> List[Dict[str, Any]]:
36 | """
37 | Replace text matching a regular expression with a provided string.
38 |
39 | Args:
40 | request (HttpRequest): The HTTP request object.
41 | events (Union[QuerySet, List[Dict[str, Any]]]): The result set to operate on.
42 | argv (List[str]): List of command-line arguments.
43 | environment (Dict[str, Any]): Dictionary used as a jinja2 environment (context) for rendering the arguments of a command.
44 |
45 | Returns:
46 | List[Dict[str, Any]]: A list of dictionaries with the specified text replaced.
47 | """
48 | events = resolve(events)
49 | args = replace.parser.parse_args(argv[1:])
50 | field = args.field
51 | expression = re.compile(args.expression)
52 | replacement = args.replacement
53 | for event in events:
54 | event[field] = expression.sub(replacement, event[field])
55 | yield event
56 |
--------------------------------------------------------------------------------
/users/views.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2025 All rights reserved.
2 | # This file is part of the Delve project, which is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).
3 | # See the LICENSE file in the root of this repository for details.
4 |
5 | from django.contrib.auth import get_user_model
6 | from django.shortcuts import render
7 | from django.contrib.auth.forms import UserCreationForm
8 | from django.urls import reverse_lazy
9 | from django.views.generic import CreateView
10 | from django.http import HttpResponse
11 | from django.contrib.auth.decorators import login_required
12 | from django.contrib import messages
13 |
14 |
15 | from .forms import (
16 | UserProfileForm,
17 | )
18 |
19 | # class CustomUserCreationForm(UserCreationForm):
20 | # class Meta:
21 | # model = get_user_model()
22 | # fields = [
23 | # 'username',
24 | # 'password1',
25 | # 'password2',
26 | # ]
27 |
28 | # class SignUpView(CreateView):
29 | # form_class = CustomUserCreationForm
30 | # success_url = reverse_lazy("login")
31 | # template_name = "registration/signup.html"
32 |
33 |
34 | def http_405():
35 | return HttpResponse(
36 | "