~widelands-dev/widelands-website/trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#!/usr/bin/env python -tt
# encoding: utf-8
#
# File: update_help.py
#
# Created by Holger Rapp on 2009-02-26.
# Copyright (c) 2009 HolgerRapp@gmx.net. All rights reserved.
#
# Last Modified: $Date$
#

from ...models import Worker as WorkerModel
from ...models import Tribe as TribeModel
from ...models import Ware as WareModel
from ...models import Building as BuildingModel

from django.core.files import File
from django.core.management.base import BaseCommand, CommandError

import os
import sys
from os import makedirs, path
import shutil
import re
import json
import subprocess
import collections

from django.conf import settings

from widelandslib.tribe import *
from widelandslib.make_flow_diagram import make_all_subgraphs


class TribeParser(object):
    map_mouseover_pattern = re.compile(
        r'(?P<beginning>.*href="../../(?P<type>[^/]+)s/(?P<name>[^/]+)/".*")&lt;TABLE&gt;(?P<rest>.*)')

    def __init__(self, name):
        """Parses the definitions for one tribe and generates the models.

        name - name of the tribe

        """
        self._delete_old_media_dir(
            name)  # You can deactivate this line if you don't need to clean house.

        base_directory = os.path.normpath(settings.WIDELANDS_SVN_DIR + '/data')
        json_directory = os.path.normpath(settings.MEDIA_ROOT + '/map_object_info')

        tribeinfo_file = open(os.path.normpath(
            json_directory + '/tribe_' + name + '.json'), 'r')
        tribeinfo = json.load(tribeinfo_file)

        self._tribe = Tribe(tribeinfo, json_directory)
        # Generate the Tribe
        self._to = TribeModel.objects.get_or_create(name=name.lower())[0]
        self._to.displayname = tribeinfo['descname']
        self._to.descr = tribeinfo['tooltip']
        # copy icon
        dn = os.path.normpath('%s/wlhelp/img/%s/' %
                              (settings.MEDIA_ROOT, tribeinfo['name']))
        try:
            os.makedirs(dn)
        except OSError, o:
            if o.errno != 17:
                raise
        new_name = path.join(dn, 'icon.png')
        file = os.path.normpath(base_directory + '/' + tribeinfo['icon'])
        shutil.copy(file, new_name)
        self._to.icon_url = path.normpath(
            '%s/%s' % (settings.MEDIA_URL, new_name[len(settings.MEDIA_ROOT):]))
        self._to.save()

    def parse(self, tribename, base_directory, json_directory):
        """Put all data into the database."""
        self._delete_old_data(
            tribename)  # You can deactivate this line if you don't need to clean house.

        wares_file = open(os.path.normpath(
            json_directory + '/' + tribename + '_wares.json'), 'r')
        self._parse_wares(base_directory, json.load(wares_file))

        workers_file = open(os.path.normpath(
            json_directory + '/' + tribename + '_workers.json'), 'r')
        self._parse_workers(base_directory, json.load(workers_file))

        buildings_file = open(os.path.normpath(
            json_directory + '/' + tribename + '_buildings.json'), 'r')
        self._parse_buildings(base_directory, json.load(buildings_file))

    def graph(self):
        """Make all graphs."""
        tdir = make_all_subgraphs(self._tribe)
        for obj, cls in [(WorkerModel, 'workers'),
                         (BuildingModel, 'buildings'),
                         (WareModel, 'wares')]:
            for inst in obj.objects.all().filter(tribe=self._to):
                try:
                    fpath = path.join(tdir, 'help/%s/%s/%s/' %
                                      (self._tribe.name, cls, inst.name))
                    url = self._copy_picture(
                        path.join(fpath, 'menu.png'), inst.name, 'graph.png')
                    inst.graph_url = url
                    inst.imagemap = open(path.join(fpath, 'map.map')).read()
                    inst.imagemap = self.map_mouseover_pattern.sub(
                        r"\1Show the \2 \3\4", inst.imagemap)
                    inst.save()
                except Exception, e:
                    print 'Exception while handling', cls, 'of', self._tribe.name, ':', inst.name
                    print type(e), e, repr(e)

        shutil.rmtree(tdir)

    def _delete_old_media_dir(self, tribename):
        """Clean house, e.g. when we have renamed a map object."""

        print('Deleting old media files...')
        sdir = os.path.normpath(os.path.join(
            settings.MEDIA_ROOT, 'wlhelp/img', tribename))
        if os.path.exists(sdir):
            shutil.rmtree(sdir)

    def _delete_old_data(self, tribename):
        """Clean house, e.g. when we have renamed a map object."""

        t = TribeModel.objects.get(name=tribename)
        print('Deleting old wares...')
        for ware in WareModel.objects.filter(tribe=t):
            ware.delete()
        print('Deleting old workers...')
        for worker in WorkerModel.objects.filter(tribe=t):
            worker.delete()
        print('Deleting old buildings...')
        for building in BuildingModel.objects.filter(tribe=t):
            building.delete()

    def _copy_picture(self, file, name, fname):
        """Copy the given image into the media directory.

        file            - original path of image
        name            - name of the item (coal, iron...)
        fname           - file name of the picture

        """
        dn = os.path.normpath('%s/wlhelp/img/%s/%s/' %
                              (settings.MEDIA_ROOT, self._to.name, name))
        try:
            os.makedirs(dn)
        except OSError, o:
            if o.errno != 17:
                raise
        new_name = path.join(dn, fname)
        shutil.copy(file, new_name)

        return '%s%s' % (settings.MEDIA_URL, new_name[len(settings.MEDIA_ROOT):])

    def _parse_workers(self, base_directory, workersinfo):
        """Put the workers into the database."""
        print '  parsing workers'

        for worker in workersinfo['workers']:
            print '    ' + worker['name']
            nn = self._copy_picture(os.path.normpath(
                base_directory + '/' + worker['icon']), worker['name'], 'menu.png')

            workero = WorkerModel.objects.get_or_create(
                tribe=self._to, name=worker['name'])[0]
            workero.displayname = worker['descname']
            workero.image_url = nn

            # Help
            workero.help = worker['helptext']

            # See what the worker becomes
            if 'becomes' in worker:
                try:
                    if worker['becomes']['experience']:
                        workero.exp = worker['becomes']['experience']

                    # The worker this worker becomes to may wasn't created yet
                    workero.becomes = WorkerModel.objects.get_or_create(
                        name=worker['becomes']['name'], tribe=self._to)[0]
                except:
                    pass

            workero.save()

    def _parse_wares(self, base_directory, waresinfo):
        print '  parsing wares'

        for ware in waresinfo['wares']:
            print '    ' + ware['name']
            nn = self._copy_picture(os.path.normpath(
                base_directory + '/' + ware['icon']), ware['name'], 'menu.png')

            w = WareModel.objects.get_or_create(
                tribe=self._to, name=ware['name'])[0]
            w.displayname = ware['descname']
            w.image_url = nn

            # Help
            w.help = ware['helptext']

            w.save()

    def _parse_buildings(self, base_directory, buildingsinfo):
        def objects_with_counts(objtype, json_):
            element_set = {}
            for element in json_:
                element_set[element['name']] = str(element['amount'])
            # Sort the dictionary alphabetical. Otherwise a wrong relation will
            # be made, e.g. build_cost and build_wares in 
            # models.get_build_cost() and other functions over there.
            element_set = collections.OrderedDict(sorted(element_set.items()))
            counts = ' '.join(element_set.values())
            objects = [objtype.objects.get_or_create(name=w, tribe=self._to)[
                0] for w in element_set.keys()]
            return counts, objects

        enhancement_hierarchy = []
        print '  parsing buildings'

        for building in buildingsinfo['buildings']:
            print '    ' + building['name']
            b = BuildingModel.objects.get_or_create(
                tribe=self._to, name=building['name'])[0]
            b.displayname = building['descname']
            b.type = building['type']

            # Get the building size
            size = building['size']
            res, = [k for k, v in BuildingModel.SIZES if v == size]
            b.size = res

            nn = self._copy_picture(os.path.normpath(
                base_directory + '/' + building['icon']), building['name'], 'menu.png')
            b.image_url = nn

            # Buildcost if we have any
            if 'buildcost' in building:
                b.build_costs, b.build_wares = objects_with_counts(
                    WareModel, building['buildcost'])

            # Try to figure out who works there
            if 'workers' in building:
                b.workers_count, b.workers_types = objects_with_counts(
                    WorkerModel, building['workers'])

            # Try to figure out if this building can be enhanced
            if 'enhancement' in building:
                enhancement_hierarchy.append((b, building['enhancement']))

            b.help = building['helptext']

            # Input wares
            if 'stored_wares' in building:
                b.store_count, b.store_wares = objects_with_counts(
                    WareModel, building['stored_wares'])

            # Output wares
            if 'produced_wares' in building:
                b.output_wares = [WareModel.objects.get_or_create(name=w, tribe=self._to)[
                    0] for w in building['produced_wares']]

            # Output workers
            if 'produced_workers' in building:
                b.output_workers = [WorkerModel.objects.get_or_create(
                    name=w, tribe=self._to)[0] for w in building['produced_workers']]

            b.save()

        for b, tgt in enhancement_hierarchy:
            try:
                b.enhancement = BuildingModel.objects.get(
                    name=tgt, tribe=self._to)
            except Exception, e:
                raise
            b.save()


class Command(BaseCommand):
    help =\
        '''Regenerates and parses the json files in a current checkout. '''

    def handle(self, directory=os.path.normpath(settings.WIDELANDS_SVN_DIR + '/data'), **kwargs):

        json_directory = os.path.normpath(settings.MEDIA_ROOT + '/map_object_info')

        if not os.path.exists(json_directory):
            os.makedirs(json_directory)

        print('JSON files will be written to: ' + json_directory)

        # First, we make sure that JSON files have been generated.
        current_dir = os.path.dirname(os.path.realpath(__file__))
        is_json_valid = False
        os.chdir(settings.WIDELANDS_SVN_DIR)
        try:
            subprocess.check_call(
                [os.path.normpath('wl_map_object_info'), json_directory])
        except:
            print(
                "Error: Unable to execute 'wl_map_object_info' for generating the JSON files.")
            sys.exit(1)

        # Now we validate that they are indeed JSON files (syntax check only)
        validator_script = os.path.normpath(
            settings.WIDELANDS_SVN_DIR + '/utils/validate_json.py')
        if not os.path.isfile(validator_script):
            print("Wrong path for 'utils/validate_json.py': " +
                  validator_script + ' does not exist!')
            sys.exit(1)
        try:
            subprocess.check_call(
                [validator_script, json_directory])
            is_json_valid = True
        except:
            print('Error: JSON files are not valid.')
            sys.exit(1)

        os.chdir(current_dir)

        # We regenerate the encyclopedia only if the JSON files passed the
        # syntax check
        if is_json_valid:
            source_file = open(os.path.normpath(
                json_directory + '/tribes.json'), 'r')
            tribesinfo = json.load(source_file)

            for t in tribesinfo['tribes']:
                tribename = t['name']
                print 'updating help for tribe ', tribename
                p = TribeParser(tribename)
                p.parse(tribename, directory, json_directory)
                p.graph()