)]}'
{"bin/nova-manage":[{"author":{"_account_id":679,"name":"Kevin L. Mitchell","email":"klmitch@mit.edu","username":"klmitch"},"change_message_id":"2d593401afa22730384a5286fc39168f663c5c06","unresolved":false,"context_lines":[{"line_number":774,"context_line":"            help\u003d\u0027Maximum number of deleted rows to archive\u0027)"},{"line_number":775,"context_line":"    def archive_deleted_rows(self, max_rows\u003dNone):"},{"line_number":776,"context_line":"        \"\"\"Move up to max_rows deleted rows from production tables to shadow"},{"line_number":777,"context_line":"        tables.\"\"\""},{"line_number":778,"context_line":"        admin_context \u003d context.get_admin_context()"},{"line_number":779,"context_line":"        db.archive_deleted_rows(admin_context, max_rows)"},{"line_number":780,"context_line":""}],"source_content_type":"application/octet-stream","patch_set":20,"id":"AAAAM3%2F%2FAdE%3D","line":777,"updated":"2013-02-11 20:24:07.000000000","message":"Nit: Pull the closing triple quote onto a line by itself.","commit_id":"440d673a9880e8b917ddcc371799fbb3a0c9dbf8"},{"author":{"_account_id":679,"name":"Kevin L. Mitchell","email":"klmitch@mit.edu","username":"klmitch"},"change_message_id":"2d593401afa22730384a5286fc39168f663c5c06","unresolved":false,"context_lines":[{"line_number":776,"context_line":"        \"\"\"Move up to max_rows deleted rows from production tables to shadow"},{"line_number":777,"context_line":"        tables.\"\"\""},{"line_number":778,"context_line":"        admin_context \u003d context.get_admin_context()"},{"line_number":779,"context_line":"        db.archive_deleted_rows(admin_context, max_rows)"},{"line_number":780,"context_line":""},{"line_number":781,"context_line":""},{"line_number":782,"context_line":"class InstanceTypeCommands(object):"}],"source_content_type":"application/octet-stream","patch_set":20,"id":"AAAAM3%2F%2FAco%3D","line":779,"updated":"2013-02-11 20:24:07.000000000","message":"I see you have added conversion to int inside db.archive_deleted_rows().  This is unnecessary.  Give the argument a type of \u0027int\u0027 and a reasonable default…","commit_id":"440d673a9880e8b917ddcc371799fbb3a0c9dbf8"},{"author":{"_account_id":679,"name":"Kevin L. Mitchell","email":"klmitch@mit.edu","username":"klmitch"},"change_message_id":"c60f07bf11e414d5759085a75cda0e15c2af77c8","unresolved":false,"context_lines":[{"line_number":777,"context_line":"        tables."},{"line_number":778,"context_line":"        \"\"\""},{"line_number":779,"context_line":"        admin_context \u003d context.get_admin_context()"},{"line_number":780,"context_line":"        db.archive_deleted_rows(admin_context, max_rows)"},{"line_number":781,"context_line":""},{"line_number":782,"context_line":""},{"line_number":783,"context_line":"class InstanceTypeCommands(object):"}],"source_content_type":"application/octet-stream","patch_set":21,"id":"AAAAM3%2F%2FAXk%3D","line":780,"updated":"2013-02-11 21:04:33.000000000","message":"I want to see the integer conversion of max_rows pulled here; I don\u0027t believe it belongs in the db function.  All you need do is specify a reasonable default and use \"int(max_rows)\" here.","commit_id":"609294d8eaa7723ffa833ebfbaa44c016fa9b3dd"}],"nova/db/api.py":[{"author":{"_account_id":679,"name":"Kevin L. Mitchell","email":"klmitch@mit.edu","username":"klmitch"},"change_message_id":"2d593401afa22730384a5286fc39168f663c5c06","unresolved":false,"context_lines":[{"line_number":1721,"context_line":""},{"line_number":1722,"context_line":"def archive_deleted_rows(context, max_rows\u003dNone):"},{"line_number":1723,"context_line":"    \"\"\"Move up to max_rows rows from production tables to corresponding shadow"},{"line_number":1724,"context_line":"    tables.\"\"\""},{"line_number":1725,"context_line":"    IMPL.archive_deleted_rows(context, max_rows\u003dmax_rows)"}],"source_content_type":"text/x-python","patch_set":20,"id":"AAAAM3%2F%2FAc4%3D","line":1724,"updated":"2013-02-11 20:24:07.000000000","message":"Triple-quote nit applies here; sorry, I should have picked up on it the first time…","commit_id":"440d673a9880e8b917ddcc371799fbb3a0c9dbf8"}],"nova/db/sqlalchemy/api.py":[{"author":{"_account_id":679,"name":"Kevin L. Mitchell","email":"klmitch@mit.edu","username":"klmitch"},"change_message_id":"c045a149957c0e03cf7b4df5a2618d95aa3fd3b9","unresolved":false,"context_lines":[{"line_number":4825,"context_line":"            for row in conn.execute(query):"},{"line_number":4826,"context_line":"                insert_statement \u003d shadow_table.insert().values(**row)"},{"line_number":4827,"context_line":"                result \u003d conn.execute(insert_statement)"},{"line_number":4828,"context_line":"                assert result.rowcount \u003d\u003d 1"},{"line_number":4829,"context_line":"                try:"},{"line_number":4830,"context_line":"                    del_statement \u003d table.delete(table.c.id \u003d\u003d row.id)"},{"line_number":4831,"context_line":"                except AttributeError:"}],"source_content_type":"text/x-python","patch_set":18,"id":"AAAAM3%2F%2FAmM%3D","line":4828,"updated":"2013-02-11 19:13:11.000000000","message":"So, this function is called by the API?  What happens if this assertion fails?  What catches the exception?","commit_id":"a7e0262059bf320f155c177b5a01b5bc9d6be23a"},{"author":{"_account_id":6172,"name":"Boris Pavlovic","email":"boris@pavlovic.me","username":"boris-42"},"change_message_id":"5d57c7b73804d57d0e040fc59e75b82e3a35f031","unresolved":false,"context_lines":[{"line_number":4814,"context_line":"    rows_archived \u003d 0"},{"line_number":4815,"context_line":"    for tablename in tablenames:"},{"line_number":4816,"context_line":"        table \u003d Table(tablename, metadata, autoload\u003dTrue)"},{"line_number":4817,"context_line":"        query \u003d select([table], table.c.deleted !\u003d 0).\\"},{"line_number":4818,"context_line":"                       limit(max_rows - rows_archived)"},{"line_number":4819,"context_line":"        shadow_tablename \u003d \"shadow_\" + tablename"},{"line_number":4820,"context_line":"        try:"}],"source_content_type":"text/x-python","patch_set":22,"id":"AAAAM3%2F%2FALM%3D","line":4817,"updated":"2013-02-11 22:20:06.000000000","message":"This will produce Exception in postgresql...\n\n`deleted` columns have type of `id` column. And in Volume and Snapshot it is String. And postgresql doesn\u0027t allow you to compare String with Integer..\n\nTake a look to model_query method (there is a pretty approach to avoid this)","commit_id":"992285ddc97840c84f517e6a52997c779f2f66f5"},{"author":{"_account_id":6172,"name":"Boris Pavlovic","email":"boris@pavlovic.me","username":"boris-42"},"change_message_id":"8f6e3cd024aadd81adfdaafc48c93ba179150f78","unresolved":false,"context_lines":[{"line_number":4803,"context_line":"            return 0"},{"line_number":4804,"context_line":"    except AttributeError:"},{"line_number":4805,"context_line":"        # Note(dripton): dns_domains is the only table that lacks an id column."},{"line_number":4806,"context_line":"        return \"\""},{"line_number":4807,"context_line":"    # NOTE(boris-42): There is only one other type that is used as id (String)"},{"line_number":4808,"context_line":"    return \"\""},{"line_number":4809,"context_line":""}],"source_content_type":"text/x-python","patch_set":23,"id":"AAAAM3%2F%2B%2FdA%3D","line":4806,"updated":"2013-02-12 09:25:40.000000000","message":"Type of DNSDomain.deleted is Boolean not String. \nSo you should return False not \"\".","commit_id":"0b83d15de7f42633b37be5af979a96eee9d30034"},{"author":{"_account_id":6172,"name":"Boris Pavlovic","email":"boris@pavlovic.me","username":"boris-42"},"change_message_id":"8f6e3cd024aadd81adfdaafc48c93ba179150f78","unresolved":false,"context_lines":[{"line_number":4804,"context_line":"    except AttributeError:"},{"line_number":4805,"context_line":"        # Note(dripton): dns_domains is the only table that lacks an id column."},{"line_number":4806,"context_line":"        return \"\""},{"line_number":4807,"context_line":"    # NOTE(boris-42): There is only one other type that is used as id (String)"},{"line_number":4808,"context_line":"    return \"\""},{"line_number":4809,"context_line":""},{"line_number":4810,"context_line":""}],"source_content_type":"text/x-python","patch_set":23,"id":"AAAAM3%2F%2B%2Fc0%3D","line":4807,"updated":"2013-02-12 09:25:40.000000000","message":"I think that you should get real default value of deleted column, because your code is long term solution. And we will be not sure in every moment that we are using for id\u0027s only Integer or String types.","commit_id":"0b83d15de7f42633b37be5af979a96eee9d30034"},{"author":{"_account_id":6172,"name":"Boris Pavlovic","email":"boris@pavlovic.me","username":"boris-42"},"change_message_id":"8f6e3cd024aadd81adfdaafc48c93ba179150f78","unresolved":false,"context_lines":[{"line_number":4837,"context_line":"            # No corresponding shadow table; skip it."},{"line_number":4838,"context_line":"            continue"},{"line_number":4839,"context_line":"        # Group the insert and delete in a transaction."},{"line_number":4840,"context_line":"        with conn.begin():"},{"line_number":4841,"context_line":"            for row in conn.execute(query):"},{"line_number":4842,"context_line":"                insert_statement \u003d shadow_table.insert().values(**row)"},{"line_number":4843,"context_line":"                result \u003d conn.execute(insert_statement)"}],"source_content_type":"text/x-python","patch_set":23,"id":"AAAAM3%2F%2B%2Fcg%3D","line":4840,"updated":"2013-02-12 09:25:40.000000000","message":"Also. This will work slow, In this case it is better to use batch insert (we already have id\u0027s so it is possible)  and batch delete (where id in ..).","commit_id":"0b83d15de7f42633b37be5af979a96eee9d30034"},{"author":{"_account_id":6172,"name":"Boris Pavlovic","email":"boris@pavlovic.me","username":"boris-42"},"change_message_id":"7fe81161aaf0e9a777e9caf13b9eb2ee2d8a993d","unresolved":false,"context_lines":[{"line_number":4841,"context_line":"            # No corresponding shadow table; skip it."},{"line_number":4842,"context_line":"            continue"},{"line_number":4843,"context_line":"        # Group the insert and delete in a transaction."},{"line_number":4844,"context_line":"        with conn.begin():"},{"line_number":4845,"context_line":"            # TODO(dripton) It might be faster to do a batch insert and then"},{"line_number":4846,"context_line":"            # a batch delete.  I don\u0027t see how to do a batch insert with"},{"line_number":4847,"context_line":"            # dynamic columns in SQLAlchemy 0.7, though.  Revisit when Nova"}],"source_content_type":"text/x-python","patch_set":25,"id":"AAAAM3%2F%2B%2Bfs%3D","line":4844,"updated":"2013-02-12 18:27:03.000000000","message":"No we don\u0027t need to wait for anything it is basic sqlalchemy low-level functionality. \n\nLet we have tbl and corresponding shadow_tbl.\n\nYou need only to do few simple steps:\n  1) create select statement: \n    select_statement \u003d tbl.select().where.....\n  2) bulk get all rows:\n    rows \u003d conn.execute(select_statement).fetchall()\n  3) bulk insert it into shadow_tbl:\n    conn.execute(shadow_tbl.insert(), rows)\n  4) bulk delete from tbl: \n    a) collect all ids from rows in array `ids`\n    b) delete_statement \u003d tbl.delete().where(tbl.c.id.in_(ids))\n    c) conn.execute(delete_statement)\nFIN.\n\nIt will work much faster, because there is only 3 requests and there is no sqlalchemy overhead.","commit_id":"beadbdeeefe20809b0bdce2a5b4d2dc321c65061"},{"author":{"_account_id":679,"name":"Kevin L. Mitchell","email":"klmitch@mit.edu","username":"klmitch"},"change_message_id":"8f6136973ead69076cd3b8079e864897925035b6","unresolved":false,"context_lines":[{"line_number":4852,"context_line":"            # \"domain\" rather than \"id\""},{"line_number":4853,"context_line":"            query \u003d select([table],"},{"line_number":4854,"context_line":"                           table.c.deleted !\u003d default_deleted_value).\\"},{"line_number":4855,"context_line":"                           order_by(table.c.domain).\\"},{"line_number":4856,"context_line":"                           limit(max_rows)"},{"line_number":4857,"context_line":"        rows \u003d conn.execute(query).fetchall()"},{"line_number":4858,"context_line":"        if rows:"}],"source_content_type":"text/x-python","patch_set":28,"id":"AAAAM3%2F%2B8MI%3D","line":4855,"updated":"2013-02-13 20:21:15.000000000","message":"Consider introspecting this earlier.  I.e., just before the \u0027with\u0027, add:\n\n    try:\n        table_id_column \u003d table.c.id\n    except AttributeError:\n        table_id_column \u003d table.c.domain\n\nThen you can use \"table_id_column\" and avoid needing these internal try/except AttributeError statements.  It also reduces the code duplication, making this function a little bit more understandable and maintainable…","commit_id":"07b7a2cd9264b0ed02b73b96da29ce00dc9322f4"},{"author":{"_account_id":679,"name":"Kevin L. Mitchell","email":"klmitch@mit.edu","username":"klmitch"},"change_message_id":"8f6136973ead69076cd3b8079e864897925035b6","unresolved":false,"context_lines":[{"line_number":4858,"context_line":"        if rows:"},{"line_number":4859,"context_line":"            insert_statement \u003d shadow_table.insert()"},{"line_number":4860,"context_line":"            result \u003d conn.execute(insert_statement, rows)"},{"line_number":4861,"context_line":"            assert result.rowcount \u003e\u003d 1"},{"line_number":4862,"context_line":"            keys \u003d []"},{"line_number":4863,"context_line":"            try:"},{"line_number":4864,"context_line":"                for row in rows:"}],"source_content_type":"text/x-python","patch_set":28,"id":"AAAAM3%2F%2B8L4%3D","line":4861,"updated":"2013-02-13 20:21:15.000000000","message":"I still am not sure I like this use of assert.  Perhaps it comes from my C background, but I just do not like using assert within library code like this.  I\u0027d almost rather you have a test and do your own raise here.  Or perhaps just exit the function silently if there is a failure, which is what you do if the table doesn\u0027t exist…","commit_id":"07b7a2cd9264b0ed02b73b96da29ce00dc9322f4"},{"author":{"_account_id":679,"name":"Kevin L. Mitchell","email":"klmitch@mit.edu","username":"klmitch"},"change_message_id":"ca5456dd0ea19c564cc3602ec57529e5c899027c","unresolved":false,"context_lines":[{"line_number":4859,"context_line":"            try:"},{"line_number":4860,"context_line":"                for row in rows:"},{"line_number":4861,"context_line":"                    keys.append(row.id)"},{"line_number":4862,"context_line":"                del_statement \u003d table.delete(column.in_(keys))"},{"line_number":4863,"context_line":"            except AttributeError:"},{"line_number":4864,"context_line":"                # We have one table (dns_domains) where the key is called"},{"line_number":4865,"context_line":"                # \"domain\" rather than \"id\""}],"source_content_type":"text/x-python","patch_set":29,"id":"AAAAM3%2F%2B8FM%3D","line":4862,"updated":"2013-02-13 20:54:19.000000000","message":"Well, this isn\u0027t there, yet; I hadn\u0027t noticed the row.id/row.domain, or I would have suggested adding to the \"column \u003d\" try/except above an \"attr \u003d \u0027id\u0027\"/\"attr \u003d \u0027domain\u0027\".  With that, you would be able to drop this try/except and just use:\n\n    for row in rows:\n        keys.append(getattr(row, attr))\n    del_statement \u003d table.delete(column.in_(keys))","commit_id":"be6a6d927023c47b0504ba017167f26cd2d334f2"},{"author":{"_account_id":6172,"name":"Boris Pavlovic","email":"boris@pavlovic.me","username":"boris-42"},"change_message_id":"52b52b4e96b7e32885699a459b236e0897816508","unresolved":false,"context_lines":[{"line_number":4802,"context_line":"    # from the column, but I don\u0027t see a way to do that in the low-level APIs"},{"line_number":4803,"context_line":"    # of SQLAlchemy 0.7.  0.8 has better introspection APIs, which we should"},{"line_number":4804,"context_line":"    # use when Nova is ready to require 0.8."},{"line_number":4805,"context_line":"    try:"},{"line_number":4806,"context_line":"        if isinstance(table.c.id.type, Integer):"},{"line_number":4807,"context_line":"            return 0"},{"line_number":4808,"context_line":"    except AttributeError:"}],"source_content_type":"text/x-python","patch_set":30,"id":"AAAAM3%2F%2B78U%3D","line":4805,"updated":"2013-02-13 21:42:48.000000000","message":"there is code in model_query(): \n\nin your case it is:\ntable.c.deleted.default.arg","commit_id":"eba1f6b8452fb83459d5f5d45cc7e2321fefdfcf"},{"author":{"_account_id":6172,"name":"Boris Pavlovic","email":"boris@pavlovic.me","username":"boris-42"},"change_message_id":"b9db7f47c56183c1792b3a04793de61ab2e8a6ee","unresolved":false,"context_lines":[{"line_number":4802,"context_line":"    # from the column, but I don\u0027t see a way to do that in the low-level APIs"},{"line_number":4803,"context_line":"    # of SQLAlchemy 0.7.  0.8 has better introspection APIs, which we should"},{"line_number":4804,"context_line":"    # use when Nova is ready to require 0.8."},{"line_number":4805,"context_line":"    try:"},{"line_number":4806,"context_line":"        if isinstance(table.c.id.type, Integer):"},{"line_number":4807,"context_line":"            return 0"},{"line_number":4808,"context_line":"    except AttributeError:"}],"source_content_type":"text/x-python","patch_set":31,"id":"AAAAM3%2F%2B6Us%3D","line":4805,"updated":"2013-02-14 18:08:25.000000000","message":"Why do you don\u0027t use table.c.deleted.default.arg ? I know that it works in this case?","commit_id":"23ec615f4991cf9d6039d836e75fb40e8a77bb95"}],"nova/db/sqlalchemy/migrate_repo/versions/153_add_shadow_tables.py":[{"author":{"_account_id":1653,"name":"garyk","email":"gkotton@vmware.com","username":"garyk"},"change_message_id":"c1095cca2ebf2473ba1e2b8af323034e8c4c3710","unresolved":false,"context_lines":[{"line_number":1,"context_line":"# vim: tabstop\u003d4 shiftwidth\u003d4 softtabstop\u003d4"},{"line_number":2,"context_line":""},{"line_number":3,"context_line":"# Copyright 2012 OpenStack LLC."},{"line_number":4,"context_line":"#"},{"line_number":5,"context_line":"#    Licensed under the Apache License, Version 2.0 (the \"License\"); you may"},{"line_number":6,"context_line":"#    not use this file except in compliance with the License. You may obtain"}],"source_content_type":"text/x-python","patch_set":10,"id":"AAAAM3%2F%2FMyg%3D","line":3,"updated":"2013-02-03 14:39:48.000000000","message":"2013","commit_id":"5c9ecb150378c2e002a8cf3d52e934b8da04ccc5"}],"nova/db/sqlalchemy/migrate_repo/versions/154_add_shadow_tables.py":[{"author":{"_account_id":6172,"name":"Boris Pavlovic","email":"boris@pavlovic.me","username":"boris-42"},"change_message_id":"c350737fc1e6c99f7f4f589a1b21ed7b63d408aa","unresolved":false,"context_lines":[{"line_number":23,"context_line":"LOG \u003d logging.getLogger(__name__)"},{"line_number":24,"context_line":""},{"line_number":25,"context_line":""},{"line_number":26,"context_line":"table_names \u003d ["},{"line_number":27,"context_line":"    \u0027aggregates\u0027,"},{"line_number":28,"context_line":"    \u0027console_pools\u0027,"},{"line_number":29,"context_line":"    \u0027instance_types\u0027,"}],"source_content_type":"text/x-python","patch_set":30,"id":"AAAAM3%2F%2B60E%3D","line":26,"updated":"2013-02-14 14:52:07.000000000","message":"This is equal to: \n\n  meta \u003d MetaData(engine)\n  meta.reflect(engine)\n  table_names \u003d meta.tables.keys()","commit_id":"eba1f6b8452fb83459d5f5d45cc7e2321fefdfcf"}],"nova/tests/test_db_api.py":[{"author":{"_account_id":679,"name":"Kevin L. Mitchell","email":"klmitch@mit.edu","username":"klmitch"},"change_message_id":"8f6136973ead69076cd3b8079e864897925035b6","unresolved":false,"context_lines":[{"line_number":1938,"context_line":"        rows4 \u003d self.conn.execute(query2).fetchall()"},{"line_number":1939,"context_line":"        self.assertEqual(len(rows4), 1)"},{"line_number":1940,"context_line":""},{"line_number":1941,"context_line":"    def tearDown(self):"},{"line_number":1942,"context_line":"        super(ArchiveTestCase, self).tearDown()"},{"line_number":1943,"context_line":"        delete_statement1 \u003d self.table1.delete("},{"line_number":1944,"context_line":"                                self.table1.c.uuid.in_(self.uuidstrs))"}],"source_content_type":"text/x-python","patch_set":28,"id":"AAAAM3%2F%2B8K0%3D","line":1941,"updated":"2013-02-13 20:21:15.000000000","message":"femtonit: I like to have tearDown() just below setUp(), so it\u0027s easier to see the clean-up routines.  After all, many test classes don\u0027t have their own tearDown(), even when they have their own setUp()…","commit_id":"07b7a2cd9264b0ed02b73b96da29ce00dc9322f4"}],"nova/tests/test_migrations.py":[{"author":{"_account_id":2750,"name":"Sean Dague","email":"sean@dague.net","username":"sdague"},"change_message_id":"547f06aebd9a8cc1ebf54e290c046d1c361babd6","unresolved":false,"context_lines":[{"line_number":635,"context_line":"    # migration 154, add shadow tables for deleted data"},{"line_number":636,"context_line":"    # There are 53 shadow tables but we only test one"},{"line_number":637,"context_line":"    # There are additional tests that use other shadow tables in test_db_api.py"},{"line_number":638,"context_line":"    def _prerun_154(self, engine):"},{"line_number":639,"context_line":"        data \u003d {"},{"line_number":640,"context_line":"            \u0027domain\u0027: \u0027fake test domain\u0027,"},{"line_number":641,"context_line":"            \u0027deleted\u0027: False,"}],"source_content_type":"text/x-python","patch_set":30,"id":"AAAAM3%2F%2B7BM%3D","line":638,"updated":"2013-02-14 11:35:54.000000000","message":"So even though there are tests somewhere else, I still have a rule of twos on this path, always insert at least 2 rows anytime we do a test here because the cross interactions are sometimes interesting. Make the 2nd one a deleted: True","commit_id":"eba1f6b8452fb83459d5f5d45cc7e2321fefdfcf"},{"author":{"_account_id":2750,"name":"Sean Dague","email":"sean@dague.net","username":"sdague"},"change_message_id":"547f06aebd9a8cc1ebf54e290c046d1c361babd6","unresolved":false,"context_lines":[{"line_number":645,"context_line":"        self.assertEqual(data[\u0027domain\u0027], result.inserted_primary_key[0])"},{"line_number":646,"context_line":"        return data"},{"line_number":647,"context_line":""},{"line_number":648,"context_line":"    def _check_154(self, engine, data):"},{"line_number":649,"context_line":"        dns_domains \u003d get_table(engine, \u0027dns_domains\u0027)"},{"line_number":650,"context_line":"        data[\u0027deleted\u0027] \u003d True"},{"line_number":651,"context_line":"        dns_domains.update().where(dns_domains.c.domain \u003d\u003d data[\u0027domain\u0027]).\\"}],"source_content_type":"text/x-python","patch_set":30,"id":"AAAAM3%2F%2B7A8%3D","line":648,"updated":"2013-02-14 11:35:54.000000000","message":"This check confuses me. Because all it does is ensure normal inserts, updates, and deletes work. If they didn\u0027t, the entire test suite would have crashed to the ground well before now.\n\nWith the 2 rows in the db (one deleted) I\u0027d just run your function, and check that the deleted row is in shadows, and the non deleted row remains.","commit_id":"eba1f6b8452fb83459d5f5d45cc7e2321fefdfcf"}]}
