2
2
from django .contrib .auth .models import User
3
3
from django .http import HttpResponse
4
4
from django .utils .encoding import force_str
5
+ from django .core .exceptions import ObjectDoesNotExist
6
+ from django .db .models .fields .related import ForeignKey , OneToOneField
7
+
8
+ from collections import deque
5
9
6
10
from oioioi .base .permissions import make_request_condition
7
11
from oioioi .base .utils import request_cached
@@ -90,7 +94,8 @@ def _fold_registration_models_tree(object):
90
94
the object, gets models related to the model and lists
91
95
all their fields."""
92
96
result = []
93
- objects_used = [object ]
97
+ objects_used = set ()
98
+ objects_used .add (object )
94
99
95
100
# https://docs.djangoproject.com/en/1.9/ref/models/meta/#migrating-old-meta-api
96
101
def get_all_related_objects (_meta ):
@@ -100,16 +105,16 @@ def get_all_related_objects(_meta):
100
105
if (f .one_to_many or f .one_to_one ) and f .auto_created and not f .concrete
101
106
]
102
107
103
- objs = [
104
- getattr (object , rel . get_accessor_name ())
105
- for rel in get_all_related_objects (object . _meta )
106
- if hasattr ( object , rel .get_accessor_name ())
107
- ]
108
+ objs = deque ()
109
+ for rel in get_all_related_objects (object . _meta ):
110
+ if hasattr (object , rel . get_accessor_name ()):
111
+ objs . append ( getattr ( object , rel .get_accessor_name () ))
112
+
108
113
while objs :
109
- current = objs .pop ( 0 )
114
+ current = objs .popleft ( )
110
115
if current is None :
111
116
continue
112
- objects_used .append (current )
117
+ objects_used .add (current )
113
118
114
119
for field in current ._meta .fields :
115
120
if (
@@ -123,17 +128,59 @@ def get_all_related_objects(_meta):
123
128
if not field .auto_created :
124
129
if field .remote_field is None :
125
130
result += [(obj , field )]
131
+
126
132
return result
127
133
128
134
129
- def serialize_participants_data (request , participants ):
135
+ def get_related_paths (model , prefix = '' , depth = 5 , visited = None ):
136
+ if visited is None :
137
+ visited = set ()
138
+ if model in visited or depth == 0 :
139
+ return []
140
+
141
+ visited .add (model )
142
+ paths = []
143
+ try :
144
+ for field in model ._meta .get_fields ():
145
+ if isinstance (field , (ForeignKey , OneToOneField )) and not field .auto_created :
146
+ related_model = field .related_model
147
+ if related_model == Participant :
148
+ continue # skip backward pointer to Participant
149
+
150
+ full_path = f"{ prefix } __{ field .name } " if prefix else field .name
151
+ paths .append (full_path )
152
+
153
+ paths .extend (
154
+ get_related_paths (related_model , prefix = full_path , depth = depth - 1 , visited = visited )
155
+ )
156
+ finally :
157
+ visited .remove (model )
158
+
159
+ return paths
160
+
161
+
162
+ def serialize_participants_data (request ):
130
163
"""Serializes all personal data of participants to a table.
131
- :param participants: A QuerySet from table participants.
132
164
"""
133
-
134
- if not participants . exists () :
165
+ participant = Participant . objects . filter ( contest = request . contest ). first ()
166
+ if participant is None :
135
167
return {'no_participants' : True }
136
168
169
+ try : # Check if registration model exists
170
+ registration_model_instance = participant .registration_model
171
+ registration_model_class = registration_model_instance .__class__
172
+ registration_model_name = registration_model_instance ._meta .get_field ('participant' ).remote_field .related_name
173
+
174
+ related = get_related_paths (registration_model_class , prefix = registration_model_name , depth = 10 )
175
+ related .extend (['user' , 'contest' , registration_model_name ])
176
+ participants = (
177
+ Participant .objects
178
+ .filter (contest = request .contest )
179
+ .select_related (* related )
180
+ )
181
+ except ObjectDoesNotExist : # It doesn't, so no need to select anything
182
+ participants = Participant .objects .filter (contest = request .contest )
183
+
137
184
display_email = request .contest .controller .show_email_in_participants_data
138
185
139
186
keys = ['username' , 'user ID' , 'first name' , 'last name' ] + (
@@ -144,9 +191,11 @@ def key_name(attr):
144
191
(obj , field ) = attr
145
192
return str (obj .__class__ .__name__ ) + ": " + field .verbose_name .title ()
146
193
194
+ folded_participants = [(participant , _fold_registration_models_tree (participant )) for participant in participants ]
195
+
147
196
set_of_keys = set (keys )
148
- for participant in participants :
149
- for key in map (key_name , _fold_registration_models_tree ( participant ) ):
197
+ for participant , folded in folded_participants :
198
+ for key in map (key_name , folded ):
150
199
if key not in set_of_keys :
151
200
set_of_keys .add (key )
152
201
keys .append (key )
@@ -156,8 +205,8 @@ def key_value(attr):
156
205
return (key_name ((obj , field )), field .value_to_string (obj ))
157
206
158
207
data = []
159
- for participant in participants :
160
- values = dict (list (map (key_value , _fold_registration_models_tree ( participant ) )))
208
+ for participant , folded in folded_participants :
209
+ values = dict (list (map (key_value , folded )))
161
210
values ['username' ] = participant .user .username
162
211
values ['user ID' ] = participant .user .id
163
212
values ['first name' ] = participant .user .first_name
@@ -169,8 +218,8 @@ def key_value(attr):
169
218
return {'keys' : keys , 'data' : data }
170
219
171
220
172
- def render_participants_data_csv (request , participants , name ):
173
- data = serialize_participants_data (request , participants )
221
+ def render_participants_data_csv (request , name ):
222
+ data = serialize_participants_data (request )
174
223
response = HttpResponse (content_type = 'text/csv' )
175
224
response ['Content-Disposition' ] = 'attachment; filename=%s-%s.csv' % (
176
225
name ,
0 commit comments